How to convert Character Numerals into Numeric format? - abap

I am trying to convert some String of Characters into Numeric form and then further incrementing it.
For Example : XX1-XXXXX.01.01.01.01 should be incremented to XX1-XXXXX.01.01.01.02 and if it reaches to XX1-XXXXX.01.01.01.99 then next increment should be XX1-XXXXX.01.01.02.00 and it continues for next node also.
XX1-XXXXX.01.01.01.01 is in Character Format.
Any Possible Solution?

This one is not compatible down to ABAP 700 like szakos but I want to bring in another solution:
FORM increment
CHANGING
fcw_out TYPE string.
DATA lw_as_n TYPE n LENGTH 8.
SPLIT fcw_out AT '.' INTO DATA(lw_start) DATA(lw_rest).
REPLACE ALL OCCURRENCES OF '.' IN lw_rest WITH ''.
lw_as_n = lw_rest + 1.
fcw_out = |{ lw_start }.{ lw_as_n(2) }.{ lw_as_n+2(2) }.{ lw_as_n+4(2) }.{ lw_as_n+6(2) }|.
ENDFORM.
" ------------------------------------------------
" Test run
" ------------------------------------------------
DATA(w_str) = `XX1-XXXXX.01.01.01.99`.
PERFORM increment CHANGING w_str. " ==> XX1-XXXXX.01.01.02.00

You can achieve this by converting the numerical parts of the node to a number and write it back after increasing. Simple example code:
DATA: l_node TYPE c LENGTH 21,
l_node_new TYPE c LENGTH 21,
l_help TYPE c LENGTH 11,
l_num TYPE n LENGTH 8,
l_node_pos TYPE int4,
l_num_pos TYPE int4.
l_node_new = l_node = 'XX1-XXXXX.01.01.01.01'.
l_help = l_node+10(*).
REPLACE ALL OCCURRENCES OF '.' IN l_help WITH ''.
CONDENSE l_help.
l_num = l_help.
DO 100 TIMES.
ADD 1 TO l_num.
ENDDO.
DO 4 TIMES.
l_node_pos = 10 + ( sy-index - 1 ) * 3.
l_num_pos = 0 + ( sy-index - 1 ) * 2.
l_node_new+l_node_pos(2) = l_num+l_num_pos(2).
ENDDO.
WRITE / l_node.
WRITE / l_node_new.
The output is:
The ABAP version was 700 so this is the "old" syntax, but should work on newer versions.

Related

How do you detect blank lines in Fortran?

Given an input that looks like the following:
123
456
789
42
23
1337
3117
I want to iterate over this file in whitespace-separated chunks in Fortran (any version is fine). For example, let's say I wanted to take the average of each chunk (e.g. mean(123, 456, 789) then mean(42, 23, 1337) then mean(31337)).
I've tried iterating through the file normally (e.g. READ), reading in each line as a string and then converting to an int and doing whatever math I want to do on each chunk. The trouble here is that Fortran "helpfully" ignores blank lines in my text file - so when I try and compare against the empty string to check for the blank line, I never actually get a .True. on that comparison.
I feel like I'm missing something basic here, since this is a typical functionality in every other modern language, I'd be surprised if Fortran didn't somehow have it.
If you're using so-called "list-directed" input (format = '*'), Fortran does special handling to spaces, commas, and blank lines.
To your point, there's a feature which is using the BLANK keyword with read
read(iunit,'(i10)',blank="ZERO",err=1,end=2) array
You can set:
blank="ZERO" will return a valid zero value if a blank is found;
blank="NULL" is the default behavior that skips blank/returns an error depending on the input format.
If all your input values are positive, you could use blank="ZERO" and then use the location of zero values to process your data.
EDIT as #vladimir-f has correctly pointed out, you not only have blanks in between lines, but also after the end of the numbers in most lines, so this strategy will not work.
You can instead load everything into an array, and process it afterwards:
program array_with_blanks
integer :: ierr,num,iunit
integer, allocatable :: array(:)
open(newunit=iunit,file='stackoverflow',form='formatted',iostat=ierr)
allocate(array(0))
do
read(iunit,'(i10)',iostat=ierr) num
if (is_iostat_end(ierr)) then
exit
else
array = [array,num]
endif
end do
close(iunit)
print *, array
end program
Just read each line as a character (but note Francescalus's comment on the format). Then read the character as an internal file.
program stuff
implicit none
integer io, n, value, sum
character (len=1000) line
n = 0
sum = 0
io = 0
open( 42, file="stuff.txt" )
do while( io == 0 )
read( 42, "( a )", iostat = io ) line
if ( io /= 0 .or. line == "" ) then
if ( n > 0 ) print *, ( sum + 0.0 ) / n
n = 0
sum = 0
else
read( line, * ) value
n = n + 1
sum = sum + value
end if
end do
close( 42 )
end program stuff
456.000000
467.333344
3117.00000

Aligning numeric values on left with WRITE

I'm creating a calculation table and want to align the numbers on the left under the '+'.
But somehow the first number in each column from the counter has some space before it.
How can I eliminate that space and align my table so that the left side is all in one row?
Code:
DATA: counter TYPE i,
counter2 TYPE i.
ULINE /(159).
WRITE: /1 sy-vline , '+', sy-vline.
DO 11 TIMES.
counter = sy-index - 1 .
WRITE: counter, sy-vline.
ENDDO.
ULINE /(159).
DO 11 TIMES.
counter = sy-index - 1 .
WRITE: /1 sy-vline , counter , sy-vline.
ULINE /(159).
ENDDO.
The spaces in front of the number are there because of the data type. Type i is an elementary data type and can have numbers from -2147483648 to 2147483647, which means it can be 11 characters long. Some data types have an output length that is variable, but that is not the case for i. You can see that if you click on it in your output, it should have a red outline 11 characters long.
But if you would rather have the spaces at the end of the number, then you can use 'CONVERSION_EXIT_ALPHA_OUTPUT'. But the "table outline" will still have to be just as big, since the number can have 11 characters.
DATA: counterc TYPE c LENGTH 11.
...
MOVE counter TO counterc.
CALL FUNCTION 'CONVERSION_EXIT_ALPHA_OUTPUT'
EXPORTING
input = counterc
IMPORTING
output = counterc.
...
WRITE: ... counterc ...
Alternatively, the output of a table looks way better if you use SALV. Look here for example, to see how to output a table using SALV.

How to count the words of a string without using any function in abap

Hello Experts
I need a help to count the words of string without using any function in ABAP.
We can only use do loop and if condition.
Please Help
METHODS count_words
IMPORTING
iv_text TYPE string
RETURNING
VALUE(rv_result) TYPE i.
METHOD count_words.
CONSTANTS lc_space TYPE string VALUE ` `.
DATA(lv_remaining_text) = iv_text.
DATA(lv_last_char) = ``.
DO.
IF lv_remaining_text IS INITIAL.
RETURN. " or EXIT if you omit the METHOD around this
ENDIF.
DATA(lv_next_char) = lv_remaining_text(1).
IF lv_next_char <> lc_space AND
( lv_last_char IS INITIAL OR
lv_last_char = lc_space ).
rv_result = rv_result + 1.
ENDIF.
lv_last_char = lv_next_char.
lv_remaining_text = lv_remaining_text+1.
ENDDO.
ENDMETHOD.
Please don't code like this in practice. This sort of low-level character juggling is only for educational purposes.
In real life, please use something like this:
METHOD count_words_cleanly.
rv_result = count( val = iv_text
regex = `(\s\S|^\S)` ).
ENDMETHOD.
You could count the number of spaces between your words.
You need at least STRLEN to determine the string length... without it I dont think this works. CONDENSE might be useful in some cases... give it a try without CONDENSE.
DATA a TYPE string VALUE 'MY NAME IS JOHN'.
DATA c TYPE i.
DATA d TYPE c.
DATA words TYPE i value 1.
DATA e TYPE i VALUE 0.
CONDENSE a.
c = STRLEN( a ).
DO c TIMES.
d = a+e(1).
e = e + 1.
IF d = ' '.
words = words + 1.
ENDIF.
ENDDO.
WRITE words. // just as output ... delete on demand

How to do typecasting without loosing a comma?

Task: I have data col(30) TYPE c VALUE '-1111,45'. and I need to check if this value is negative, if negative - do typecasting. But on the output I get 5 though I have to get 1111,45 without a minus and again do typecasting to the previous type (с).
REPORT Z_CAST.
data col(30) TYPE c VALUE '-1111,45'.
data numc type n.
numc = col.
if numc < 0.
numc = -1 * numc.
endif.
col = numc.
WRITE col. "Output: 5
REPORT Z_CAST.
data col(30) TYPE c VALUE '-1111,45'.
if col+0(1) EQ '-'.
WRITE col+1. "1111,45
endif.
This is the minimum character processing way to do this:
col = replace( val = col
sub = '-'
with = ' ' ).
You don't have to find the - first to replace it.
And this is the clean numeric way that ABAPers will usually choose:
DATA n TYPE p LENGTH 12 DECIMALS 2.
n = col.
n = abs( n ).
Note that this does not work with your example '-1111,45' though: ABAP expects a point . as decimal separator, but your number uses a localized format with comma ,, so you would have to normalize the number first.

How to write number with sign on the left and thousands separator point

I am holding the number in character format in abap. Because I have to take the minus from right to left. So I have to put the number to character and shift or using function 'CLOI_PUT_SIGN_IN_FRONT' I'm moving minus character to left.
But after assigning number to character it doesn't hold the points. I mean my number is;
1.432- (as integer)
-1432 (as character)
I want;
-1.432 (as character)
is there a shortcut for this or should I append some string operations.
Edit:
Here is what I'm doing now.
data: mustbak_t(10) TYPE c,
mustbak like zsomething-menge.
select single menge from zsomething into mustbak where something eq something.
mustbak_t = mustbak.
CALL FUNCTION 'CLOI_PUT_SIGN_IN_FRONT'
CHANGING
VALUE = mustbak_t.
write: mustbak_t.
If you're on a recent release, you could use string templates - you'll have to add some black magic to use a country that confoirms to your decimal settings, though:
DATA: l_country TYPE t005x-land,
l_text TYPE c LENGTH 15,
l_num TYPE p LENGTH 6.
SELECT SINGLE land
INTO l_country
FROM t005x
WHERE xdezp = space.
l_num = '-123456'.
l_text = |{ l_num COUNTRY = l_country }|.
WRITE: / l_text.
In this case, you need a country code to pass to the COUNTRY parameter as described in the format options. The values of the individual fields, namely T005X-XDEZP are described in detail in the country-specific formats.
tl;dr = Find any country where they use "." as a thousands separator and "," as a decimal separator and use that country settings to format the number.
You could also use classic formatting templates, but they are hard to handle unless you have a fixed-length output value:
DATA: l_text TYPE c LENGTH 15,
l_num TYPE p LENGTH 6 DECIMALS 2.
l_num = '-1234.56'.
WRITE l_num TO l_text USING EDIT MASK 'RRV________.__'.
CONDENSE l_text NO-GAPS.
WRITE: / l_text.
Here's another way, which i finally got working:
DATA: characters(18) TYPE c,
ints TYPE i VALUE -222333444.
WRITE ints TO characters. "This is it... nothing more to say.
CALL FUNCTION 'CLOI_PUT_SIGN_IN_FRONT'
CHANGING
value = characters.
WRITE characters.
Since integers are automatically printed with the thousands separator, you can simply output them to a char data object directly using WRITE TO with no aditions..... lol
DATA: currency TYPE cdcurr,
characters(18) TYPE c,
ints TYPE i VALUE -200000.
currency = ints.
WRITE currency TO characters CURRENCY 'USD' DECIMALS 0.
CALL FUNCTION 'CLOI_PUT_SIGN_IN_FRONT'
CHANGING
value = characters.
.
WRITE: / 'example',characters.
This prints your integer as specified. Must be apparently converted to a currency during the process.