2023 May 24 8:42 AM
Hey,
I have a text containing strings in capitals like this:
AGGSTALL
BAD KÖTZTING
RIED A.HAIDSTEIN
SCHMID I.LEHEN
TAUFKIRCHEN (VILS)
BAD KÖNIGSHOFEN I.GRABFELD
I have to transform them to:
Aggstall
Bad Kötzting
Ried a.Haidstein
Schmid i.Lehen
Taufkirchen (Vils)
Bad Königshofen i.Grabfeld
Google-ing I've learned about to_mixed and found here a trick that solves the second string BUT not the rest:
Do you have an idea how can I solve this?
Thanks!
Regards,
Vlad
2023 May 26 9:29 AM
Hello, try with this code :
data(lv_output_string_1) = to_mixed(
val = |{ replace( val = cv_input_string_1 sub = `.` with = `..` occ = 0 ) } |
sep = '.'
min = ( find( val = cv_input_string_1 sub = '.' ) + 1 ) ).
Works for me.
2023 May 26 9:37 AM
Hum I've just realized you have string with more than 2 words and also string with no point inside but parenthesis...
It work only with string with 2 words and with point inside, so not a solution.
Maybe you can build your own to_mixed function, somethings like this (but you have to manage the special case).
Perhaps my code is not the best way to do it, but it's working for your example.
class up_and_low_mixer definition.
public section.
class-methods mix importing str type string
returning value(r) type string.
endclass.
class up_and_low_mixer implementation.
method mix.
data table_for_string type standard table of string.
data temp_str type string.
data(working_str) = str.
working_str = to_lower( working_str ).
split working_str at ` ` into table table_for_string.
loop at table_for_string reference into data(line).
temp_str = cond #(
when line->* ca `.` " for case a.Aaaa
then to_mixed(
val = |{ replace( val = line->* sub = `.` with = `..` ) }| sep = `.` min = 2 )
when line->* ca `(` " for case (Aaa)
then to_mixed(
val = |{ replace( val = line->* sub = `(` with = `((` ) }| sep = `(` )
else " normal
to_mixed( val = line->* case = 'A' ) ).
r = r && temp_str && ` `.
endloop.
endmethod.
endclass.
" And you call your function class
data(lv_output_string_1) = up_and_low_mixer=>mix( cv_input_string_1 ).
data(lv_output_string_2) = up_and_low_mixer=>mix( cv_input_string_2 ).
...
2023 May 26 7:14 PM
lou.meron that does it! Thanks!
Could you please explain me how each of the two replace work (separately and in the cond)?
Thanks again!
2023 May 29 8:40 AM
Hi vladghitulescu, perfect if it's working for your need.
For e.g the first replace function input is "a.haidstein'" output will be "a..haidstein" .
For the second you may have input like "(vals)" and output will be "((vals)".
After, everything is done in to_mixed function. Pay attention to the "sep" and "min" parameters. It's explained like this :
to_upper, to_lower, to_mixed, from_mixed - - ABAP Keyword Documentation (sap.com)
2023 May 29 12:08 PM
Ah, very cool trick with sep & min! Thanks again lou.meron !
2023 May 26 4:01 PM
You should indicate which exact rule you are looking for.
Any character before dot must be lower case?
Any character after opening bracket must be upper case?
What else?
2023 May 26 5:26 PM
Thanks Lou, I'll check this as soon as my system is alive again (right now is down due to maintenance) and report back.
2023 May 26 5:36 PM
sandra.rossi you're right, I'll specify more.
I have this 6 examples:
AGGSTALL
BAD KÖTZTING
RIED A.HAIDSTEIN
SCHMID I.LEHEN
TAUFKIRCHEN (VILS)
BAD KÖNIGSHOFEN I.GRABFELD
AGGSTALL (= 1) is solved by genuine to_mixed.
BAD KÖTZTING (= 2) is solved by to_mixed with the replace - see the screenshot.
All others (= 3...6) are NOT solved as expected.
I stated at the beginning what I would like as output.
You can see in the screenshot from the debugger the values of lv_output_string_3...6.
Now to the rules:
I hope this describes the problem better.
2023 May 26 5:48 PM
A necessary correction of the last rule above: Every char after a space should be upper case EXCEPT when immediately followed by a dot (because chars followed by a dot should be lower case, see first rule)
2023 May 29 3:08 PM
You are using to_mixed as a nice shortcut to solve your issue, although it was not really designed for what you're trying to achieve (it's more about transforming technical names like client_number to ClientNumber, but almost not more).
If you run ABAP 7.55, PCRE can be used.
Otherwise, it's probably better to not use to_mixed, but some code straight to the point.
Based on the rules you defined (I completed with the last two ones):
I see that there are conflicting rules so let's suppose that the rule number 1 has the priority over the others.
Below example shows code for PCRE (ABAP >= 7.55) and classic code with REDUCE (ABAP >= 7.40 SP 8).
EDIT: I have added comments for the PCRE code.
CLASS ltc_main DEFINITION
FOR TESTING
DURATION SHORT
RISK LEVEL HARMLESS.
PRIVATE SECTION.
METHODS run_all_test_cases FOR TESTING.
METHODS run_one_test_case
IMPORTING
input TYPE csequence
exp TYPE csequence
RETURNING
VALUE(result) TYPE string.
METHODS with_pcre " ABAP >= 7.55
IMPORTING
input TYPE csequence
RETURNING
VALUE(result) TYPE string.
METHODS without_pcre
IMPORTING
input TYPE csequence
RETURNING
VALUE(result) TYPE string.
ENDCLASS.
CLASS ltc_main IMPLEMENTATION.
METHOD run_all_test_cases.
run_one_test_case( input = `AGGSTALL` exp = `Aggstall` ).
run_one_test_case( input = `BAD KÖTZTING` exp = `Bad Kötzting` ).
run_one_test_case( input = `RIED A.HAIDSTEIN` exp = `Ried a.Haidstein` ).
run_one_test_case( input = `SCHMID I.LEHEN` exp = `Schmid i.Lehen` ).
run_one_test_case( input = `TAUFKIRCHEN (VILS)` exp = `Taufkirchen (Vils)` ).
run_one_test_case( input = `BAD KÖNIGSHOFEN I.GRABFELD` exp = `Bad Königshofen i.Grabfeld` ).
run_one_test_case( input = `.A.b` exp = `.a.B` ). " rule 1 has priority over rule 2
run_one_test_case( input = `(a.` exp = `(a.` ). " rule 1 has priority over rule 3
run_one_test_case( input = ` a.` exp = ` a.` ). " rule 1 has priority over rule 4
run_one_test_case( input = `a.` exp = `a.` ). " rule 1 has priority over rule 5
ENDMETHOD.
METHOD run_one_test_case.
cl_abap_unit_assert=>assert_equals( act = with_pcre( input = input ) exp = exp msg = |Input: { input } -> Exp: { exp }| ).
cl_abap_unit_assert=>assert_equals( act = without_pcre( input = input ) exp = exp ).
ENDMETHOD.
METHOD with_pcre.
result = replace( val = input
pcre = `(?:`
& `(\w\.)` " subgroup $1: either word char followed by dot
& `|(?<=\.)(\w)` " subgroup $2: or word char preceded by dot
& `|(?<=\()(\w)` " subgroup $3: or word char preceded by start bracket
& `|(?<=\ )(\w)` " subgroup $4: or word char preceded by space
& `|^(\w)` " subgroup $5: or word char at start
& `|(.)` " subgroup $6: or any character
& `)`
with = `\l$1` " convert character of subgroup $1 to lower case
& `\u$2` " convert character of subgroup $2 to upper case
& `\u$3` " convert character of subgroup $3 to upper case
& `\u$4` " convert character of subgroup $4 to upper case
& `\u$5` " convert character of subgroup $5 to upper case
& `\l$6` " convert character of subgroup $6 to lower case
occ = 0 ).
ENDMETHOD.
METHOD without_pcre.
result = reduce #( init t = ``
prev = ``
char = cond string( when input <> `` then substring( val = input off = 0 len = 1 ) )
for off = 0 while off < strlen( input )
let next = cond string( when off + 1 < strlen( input ) then substring( val = input off = off + 1 len = 1 ) )
in
next t = t && COND string(
WHEN next = '.' THEN to_lower( char )
WHEN prev = '.' THEN to_upper( char )
WHEN prev = '(' THEN to_upper( char )
WHEN prev = ` ` THEN to_upper( char )
WHEN off = 0 THEN to_upper( char )
ELSE to_lower( char ) )
prev = char
char = next ).
ENDMETHOD.
ENDCLASS.
2023 May 29 4:21 PM
Love your reduce part. I hadn't thought of doing that with reduce, very cool !
2023 May 30 11:59 AM
Thanks sandra.rossi!
I'll use this one as an incentive to finally apply some RegEx in ABAP AND as a chance to implement some useful Unit Tests 🙂
I'm not quite sure though about "ABAP >= 7.55":
I'll try it anyway 😉 and report back.
Thanks again!
2023 May 30 12:54 PM
No PCRE yet 😞 so REDUCE is the way to go until then.
I have one question:
I inserted the whole class into Eclipse's "Global Class"-tab but there's a "Test Classes"-tab as well. How would one spread the methods over this tabs? Should I rather put the definition and implementations of the run_all_test_cases - method in the "Test Classes" and everything else into "Global Class"? Or only its implementation?
Thanks!
2023 May 30 6:32 PM
SAP_BASIS 750 0017/SAP_ABAP 750 0017 means ABAP 7.50 SP 17 (so it's < ABAP 7.55).
In my system, I have SAP_BASIS 757 0000 / SAP_ABA 75H 0000 so it's ABAP 7.57 (don't ask me what 7.5H is, I use then SAP_BASIS to know the actual ABAP version).
2023 May 30 6:36 PM
Yes, the whole test classes (definition + implementation) go in the "Test Classes" tab.
My code was just for quick testing, I put it directly in an executable program right below the line "REPORT ztestprogram". Don't bother creating a class pool just to run the test.
PS: bravo for using Eclipse ADT!