How to retrieve the last character of a string in ABAP – definitive edition
Many of the resources we have on ABAP might be old, but they still frequently show up on top positions of search results. I encourage you to correct incorrect information you stumble upon, even many years later.
Here is my attempt to thoroughly solve a trivial problem I needed reminding about this week – retrieving the last character of a string.
My analysis will be presented in the form of a contest between the respondents to the same question asked on SAP Q&A in 2007, with some measurements at the end. Let’s see how the answers fare in 2020. There were seven proposed solutions, but first…
the results. Because the rest is mostly rambling. If you’re interested in that, feel free to read the whole thing. But if you just want code and numbers, this is just a two-minute read:
I made some measurements of the correct approaches from the answers:
- rotate using SHIFT, return first character
The run times were be measured on a short string (guid) and a long string (5000 concatenated guids)
report zlastchar. class lcl_string_util definition create private. public section. " feel free to substitute your favorite single character abstraction types: t_last_character type c length 1. class-methods: get_using_substring importing str type string returning value(result) type t_last_character. class-methods: get_using_off_len importing str type string returning value(result) type t_last_character. class-methods get_using_shift importing value(str) type string returning value(result) type t_last_character. endclass. class lcl_profiler definition create private. public section. class-data: guid type string. class-data: n type i. class-methods: off_len, substring, shift. endclass. """""""""""""""""""""""""""""""""""" " we will measure a random short string as well as a much longer string lcl_profiler=>n = 10000. lcl_profiler=>guid = cl_system_uuid=>create_uuid_c36_static( ). data(really_long_string) = ``. do 5000 times. really_long_string = really_long_string && cl_system_uuid=>create_uuid_c36_static( ). enddo. lcl_profiler=>off_len( ). lcl_profiler=>substring( ). lcl_profiler=>shift( ). """""""""""""""""""""""""""""""""""" class lcl_string_util implementation. method get_using_substring. result = substring( off = strlen( str ) - 1 len = 1 val = str ). endmethod. method get_using_off_len. data(index_before_last) = strlen( str ) - 1. result = str+index_before_last(1). endmethod. method get_using_shift. shift str right by 1 places circular. result = str(1). endmethod. endclass. class lcl_profiler implementation. method off_len. do n times. lcl_string_util=>get_using_off_len( guid ). lcl_string_util=>get_using_off_len( really_long_string ). enddo. endmethod. method substring. do n times. lcl_string_util=>get_using_substring( guid ). lcl_string_util=>get_using_substring( really_long_string ). enddo. endmethod. method shift. do n times. lcl_string_util=>get_using_shift( guid ). lcl_string_util=>get_using_shift( really_long_string ). enddo. endmethod. endclass.
And the measurements (in microseconds):
Using offset/length or the substring function seems to be largely equivalent, very slightly favoring offset/length. Which makes it the winner!
But the shift approach really is taking over 30x as long. Still, you might say it doesn’t amount to much… it’s a few miliseconds for 10 000 iterations. Who cares?
Let’s try an even longer string by concatenating 50 000 guids. Now the shift method takes 10 seconds, while the first two approaches still only take about 11 miliseconds.
Perhaps if your application crumbles when faced with large volumes of data, you don’t have to do anything crazy to fix it. Maybe you’re just doing a basic thing incorrectly too many times.
Edit: if you’re a new abaper, make sure not to take the suggestions from the comments section too seriously.
- It’s true that the database can do a lot of heavy lifting for you, but code pushdown is not to be used for trivial things.
- Even though you can use sql directly in abap, make sure to encapsulate database access or your code will be difficult to test.
- This comment contains some more measurements of the regex-based solutions. I suggest you stick to the two fast ones above.
And now for the contest part
The problem specification
I need to get the last character of the string, like this…
‘AAAAAAAA BBBB 1 QQQQQQQQ 2’.
i need to take the last letter (2) to do more things b4.
I’m thinking to rotate the string and take the first letter, but i think will exist a FM that do my requirement.
use below logic
str = ‘test’.
data a type i.
a = strlen(str).
Amole’s solution was voted best answer and highlighted in green. I reluctantly copy-pasted the code into a report.
The code does not compile. After fixing a couple of syntax errors, I ran the report to see it print out test, which is not the last character of the word test.
When cleaned up, you can easily see that the code always prints out the original string:
report zfh. data(str) = 'test'. data(len) = strlen( str ). write str(len). " this is equal to write str+0(len). " which is equal to write str.
The author of the question mentions in a comment that they “like a lot the amole solution”. I’m not sure how to feel about that.
Lots of ways to do this. another one would be to use the function module REVERSE_STRING, or STRING_REVERSE. It is something like that. Anyway, from the name, you can guess what it does, then all you need to do is look at the first letter of the string using an offset.
The algorithm proposed by Rich is correct. As for the function module, it’s the second one:
FUNCTION STRING_REVERSE IMPORTING VALUE(STRING) TYPE ANY VALUE(LANG) TYPE ANY EXPORTING VALUE(RSTRING) TYPE ANY EXCEPTIONS TOO_SMALL.
I especially like the lang parameter here, really makes you wonder. So if you are too, this function module will count characters slightly differently in Thai. I choose to forget about this particular fact and hope that strlen does return the universally agreed-upon lengths of characters on unicode systems. I would actually like to know if I’m right about this. Feel free to let me know in the comments.
The solution gets minus points in 2020 where clean code is a thing for relying on untyped parameters and legacy exception handling. Using a static utility method would be a much better choice today.
Also, it doesn’t work if used with the data type string, only on fixed-length character types.
Given the simplicity of the problem we’re trying to solve, this is a gotcha I wouldn’t expect to encounter. The parameter claims it could be anything!
I’m sorry Rich, but I have to disqualify you. But since you still work in SAP 13 years later, it might be fun for you to reminisce on how much changed since then in the comments section.
name = ‘PRABHU’.
n = strlen(name).
n = n – 1. (n = 5)
output = name+n(1) = U.
Due to the unfortunate mixing of explanations and code without actually using comments, Prahbu’s solution does not compile and also loses some points for readability. But I can understand what he means and the algorithm is correct.
Former Member #1
one long way is
first use STRING_LENGTH FM , then subtract 1 from length
then use STRING_SPLIT_AT_POSITION and pass the length previously calculated
Plus point for correct algorithm. #1’s proposed solution would look something like this:
report zfh. data: string type string value 'ABCD', len type i, result type string. call function 'STRING_LENGTH' exporting string = string importing length = len. call function 'STRING_SPLIT_AT_POSITION' exporting string = string pos = len - 1 importing string2 = result.
these function modules also do not work with strings. I’ve already explained my concerns with this as well as usage of function modules.
First get the string lengthof the variable.
data: lv_string type char4 value ‘ABCD’,
lv_new type i,
lv_new1 type char2.
*lv_new = strlen(lv_string).
CALL FUNCTION ‘STRING_LENGTH’
string = lv_string
LENGTH = lv_new
lv_new = lv_new – 2.
lv_new1 = lv_string+lv_new(2).
You get the last two characters.
reward if useful..
Useful, but no reward. Nazeer narrowly misses the final round by misunderstanding the question.
‘AAAAAAAA BBBB 1 QQQQQQQQ 2’.
i need to take the last letter (2) to do more things b4.
He misinterprets the 2 given as the output to the author’s example input, and instead gives the solution on how to return the last 2 characters.
This underlines the importance of taking the time to ask good questions.
Nazeer gets plus points for solving his variant of the problem correctly. His code, once cleaned up, makes sense and even compiles! He also provided an alternative solution.
report zfh. data: lv_string type char4 value 'ABCD', lv_new type i, lv_new1 type char2. *lv_new = strlen( lv_string ). call function 'STRING_LENGTH' exporting string = lv_string importing length = lv_new. lv_new = lv_new - 2. lv_new1 = lv_string+lv_new(2). write lv_new1.
One way to do this is ,
- find the string length by usinf strlen(strVariableName or String) command.
- Find the string lenght-1 posotion of that string.
Would you mind if I just called you Ram? Ram’s algorithm is correct, but he does not specify exactly how to Find the string lenght-1 posotion of that string, which might be important. The ABAP +off(len) syntax might not be familiar to everyone and there might be different methods as well.
Former member #2
use the below code.
data : l_length type i,
l_data = ‘AAAAAAAA BBBB 1 QQQQQQQQ 2’.
l_length = STRLEN( l_data ) . ” here u will get the length of the field l_data.
by using the length read your variable l_data
and get the last 2 records
#2 surprises us with exactly the same misunderstanding of the question Nazeer had. His code is missing the final line of code, which is instead explained, giving us a nice hybrid solution.
from a similar StackOverflow question from 2018:
Note how the code snippets are actually readable without having to copy-paste it somewhere else first. Both solutions produce correct results. Despite its author’s confidence, the second snippet is worse in performance, but it is downvoted and there is a recent comment from Sandra Rossi explaining why it’s slower. Thank you for doing that. Your efforts are not in vain.
And now that I have this out of my system, I can go back to delivering you the intelligent enterprise. Stay tuned.