COBOL alternative to BASIC's MID and how to concat strings? - string

I'm looking for the COBOL alternative of Visual Basic's MID Function. The thing I need to do is take from 8 strings the first 5 letters and concatenate them.
I'm using Fujitsu COBOL.
Many thanks,
Yvan

Paxdiablo has given a couple of valid ways to do it. Another way would be to use reference modification in addition to the STRING verb. Complete program example follows:
IDENTIFICATION DIVISION.
PROGRAM-ID. EXAMPLE9.
DATA DIVISION.
WORKING-STORAGE SECTION.
01 STRINGS.
05 STRING1 PIC X(10) VALUE 'AAAAAAAAAA'.
05 STRING2 PIC X(10) VALUE 'BBBBBBBBBB'.
05 STRING3 PIC X(10) VALUE 'CCCCCCCCCC'.
05 STRING4 PIC X(10) VALUE 'DDDDDDDDDD'.
05 STRING5 PIC X(10) VALUE 'EEEEEEEEEE'.
05 STRING6 PIC X(10) VALUE 'FFFFFFFFFF'.
05 STRING7 PIC X(10) VALUE 'GGGGGGGGGG'.
05 STRING8 PIC X(10) VALUE 'HHHHHHHHHH'.
05 STRING-OUT PIC X(40) VALUE SPACES.
PROCEDURE DIVISION.
STRING STRING1(1:5) STRING2(1:5) STRING3(1:5) STRING4(1:5)
STRING5(1:5) STRING6(1:5) STRING7(1:5) STRING8(1:5)
DELIMITED BY SIZE
INTO STRING-OUT
DISPLAY STRING-OUT
GOBACK.
This cuts down on the verbosity quite a bit and captures the concatenation in a single statement. Best advice is to read up on the STRING verb. There are a number of innovative ways it can be used.
COBOL does not provide an exact analogue to the BASIC MID statement. You can accomplish similar operations by using some combination of STRING, UNSTRING, INSPECT and reference modification. An example of a reference modification is: SOME-VARIABLE-NAME(1:5) - the 1:5 bit specifies a substring of SOME-VARIABLE-NAME starting with the first character for a length of 5 characters. The modifiers may themselves be numeric variables. The STRING and UNSTRING verbs provide a number of features that can be quite powerful.
In general though, COBOL is not particularly good at string manipulation (some might say its not particularly good at anything - but I would disagree with that statement).

I think it goes something like this:
WORKING STORAGE SECTION.
01 NORMAL-STRING-A PIC X(80)
01 NORMAL-STRING-B PIC X(80)
01 NORMAL-STRING-C PIC X(80)
01 NORMAL-STRING-D PIC X(80)
01 NORMAL-STRING-E PIC X(80)
01 SUB-STRING.
05 FIVE PIC X(5)
05 REST PIC X(75)
01 TWENTY-FIVE-A.
05 FIVE-A PIC X(5).
05 FIVE-B PIC X(5).
05 FIVE-C PIC X(5).
05 FIVE-D PIC X(5).
05 FIVE-E PIC X(5).
01 TWENTY-FIVE-B PIC X(25).
PROCEDURE DIVISION.
MOVE NORMAL-STRING-A TO SUB-STRING.
MOVE FIVE TO FIVE-A.
MOVE NORMAL-STRING-B TO SUB-STRING.
MOVE FIVE TO FIVE-B.
MOVE NORMAL-STRING-C TO SUB-STRING.
MOVE FIVE TO FIVE-C.
MOVE NORMAL-STRING-D TO SUB-STRING.
MOVE FIVE TO FIVE-D.
MOVE NORMAL-STRING-E TO SUB-STRING.
MOVE FIVE TO FIVE-E.
MOVE TWENTY-FIVE-A TO TWENTY-FIVE-B.
Then, your string is in TWENTY-FIVE-B.
You know, I can't imagine why people thought COBOL was verbose :-)
On a more serious note, I think you can do something along these lines to achieve the same result (you may have to fiddle with the start and length parameters, it's been a while since I did any serious COBOL):
WORKING STORAGE SECTION.
01 STRING-A PIC X(80)
01 STRING-B PIC X(80)
01 STRING-C PIC X(80)
01 STRING-D PIC X(80)
01 STRING-E PIC X(80)
01 TWENTY-FIVE PIC X(25).
PROCEDURE DIVISION.
MOVE STRING-A(1:5) TO TWENTY-FIVE( 1:5).
MOVE STRING-B(1:5) TO TWENTY-FIVE( 6:5).
MOVE STRING-C(1:5) TO TWENTY-FIVE(11:5).
MOVE STRING-D(1:5) TO TWENTY-FIVE(16:5).
MOVE STRING-E(1:5) TO TWENTY-FIVE(21:5).

This substring examples page shows a few variations. An example:
* Task3 suffix(xStr,Length)
* Extract the last Length number of chars from a string
* Solution - use reference modification with start of substring
* defined as the FullStringLength - SubStringLength + 1
* In this example we get the last 13 characters.
MOVE 13 TO StrLength
DISPLAY "Task3 = " xStr2((StrSize - StrLength) + 1:StrLength)

WORKING STORAGE SECTION.
01 NORMAL-STRING-A PIC X(80)
01 NORMAL-STRING-B PIC X(80)
01 NORMAL-STRING-C PIC X(80)
01 NORMAL-STRING-D PIC X(80)
01 NORMAL-STRING-E PIC X(80)
01 TWENTY-FIVE-A.
05 FIVE-A PIC X(5).
05 FIVE-B PIC X(5).
05 FIVE-C PIC X(5).
05 FIVE-D PIC X(5).
05 FIVE-E PIC X(5).
01 TWENTY-FIVE-B REDEFINES TWENTY-FIVE-A PIC X(25).
PROCEDURE DIVISION.
MOVE NORMAL-STRING-A TO FIVE-A
MOVE NORMAL-STRING-B TO FIVE-B
MOVE NORMAL-STRING-C TO FIVE-C
MOVE NORMAL-STRING-D TO FIVE-D
MOVE NORMAL-STRING-E TO FIVE-E

Related

Finding a value in an array of arrays in excel

I'm trying to indicate if an ID from one table is listed in another, using the following formula I almost get what I want except I get false negatives when an item in Table1 has multiple links listed. I thought FIND might help but can't work it out.
=NOT(ISERROR(MATCH([#ID],Table1[LINKS],0)))
Table1
ID
LINKS
01
02
01
03
01 \n 02
04
03
Table2
ID
LINKED
01
TRUE
02
FALSE
03
TRUE
04
FALSE
Your thoughtprocess about FIND() is right. You can try:
=SUMPRODUCT(--ISNUMBER(FIND(" \n "&[#ID]&" \n "," \n "&Table1[LINKS]&" \n ")))>0
And if \n is a placeholder for a newline, just replace these literals with CHAR(10).

Reversing a string without using reverse function in COBOL

my task is to reverse a string in cobol without using the reverse function.
So far i've got this:
MOVE 20 TO LOO.
MOVE 1 TO LOP.
MOVE 20 TO LOU.
MOVE EINA01 OF FORMAT1 TO WORTTXT1.
PERFORM 20 TIMES
MOVE WORTTXT1 (LOP:1) TO B (20:LOO)
SUBTRACT 1 FROM LOO
ADD 1 TO LOP
MOVE B TO WORTTXT2 (20:LOU)
SUBTRACT 1 FROM LOU
END-PERFORM.
MOVE WORTTXT2 TO AUSA01 OF FORMAT1.
AUSA01 is the output
EINA01 the input.
The problem i have right now is: If i write "Hello" into the input field, all i get is "00000000000h" he just reverses the first letter but its supposed to look like " Hello".
As you've mentioned that the program has to reverse the spaces as well, I suggest you to modify the PERFORM loop as shown below.
PERFORM 20 TIMES
MOVE WORTTXT1(LOP:1) TO B(LOO:1)
SUBTRACT 1 FROM LOO
ADD 1 TO LOP
END-PERFORM.
Full program:
IDENTIFICATION DIVISION.
PROGRAM-ID. HELLO-WORLD.
DATA DIVISION.
WORKING-STORAGE SECTION.
01 EINA01 PIC X(20) VALUE 'Srinivasan '.
01 WORTTXT1 PIC X(20) VALUE SPACES.
01 WORTTXT2 PIC X(20) VALUE SPACES.
01 AUSA01 PIC X(20) VALUE SPACES.
01 B PIC X(20) VALUE SPACES.
01 LOO PIC 9(2) VALUE 0.
01 LOP PIC 9(2) VALUE 0.
PROCEDURE DIVISION.
MOVE 20 TO LOO.
MOVE 1 TO LOP.
MOVE EINA01 TO WORTTXT1.
PERFORM 20 TIMES
MOVE WORTTXT1(LOP:1) TO B(LOO:1)
SUBTRACT 1 FROM LOO
ADD 1 TO LOP
END-PERFORM.
MOVE B TO AUSA01.
DISPLAY AUSA01.
STOP RUN.
Note: I'm not using the data items, B, LOU & WORTTXT2 as I felt that they are not required.
Output:
nasavinirS
Try it here
You can also use INSPECT to reverse a string. Code something like this to reverse an 8-character string SRC and get the result in TGT:
MOVE x'0807060504030201' TO TGT
INSPECT TGT CONVERTING x'0102030405060708' TO SRC
Note that you can use this trick to reorder a field in any desired way.

How to change constant value in linux dynamic library?

There is a dynamic library for 64-bit linux, it contains many functions compiled from from C++ code. The code is not open source, but I have an idea how one of the functions shoul look like. It contains mathematical expression and I would like to change one of the constants in this expression.
I have some programming skills, but never looked into compiled objects and executable. The relevant part of assembly code obtained by objdump -RDC command is below. The constant of interest should be of type double and it seems it is used in multiplication command in line 7e1cc.
7e1b8: 00
7e1b9: f2 0f 59 74 24 78 mulsd 0x78(%rsp),%xmm6
7e1bf: f2 41 0f 59 f0 mulsd %xmm8,%xmm6
7e1c4: f2 0f 58 ce addsd %xmm6,%xmm1
7e1c8: f2 0f 58 ca addsd %xmm2,%xmm1
7e1cc: f2 0f 59 0d fc 0e 0c mulsd 0xc0efc(%rip),%xmm1 # 13f0d0 <typeinfo name for RestorableCreator<Model>+0x90>
7e1d3: 00
7e1d4: 48 81 c4 88 00 00 00 add $0x88,%rsp
7e1db: 66 0f 28 c1 movapd %xmm1,%xmm0
7e1df: c3 retq
I'd like to know how to find the position of this constant in the file, convert my constant to hex format and replace the value in the file to my hex value. Could anyone explain how to do this? Also suggestions about proper tools would be really valuable.
The constant is at address 0xc0efc(%rip) where %rip is the address of the next instruction, meaning 0x7e1d4. So the address is: 0xc0efc + 0x7e1d4 = 0x13F0D0 (objdump even prints that for you).
Now, examine the headers of your binary using objdump -h. That will list all the sections along with virtual addresses and file offsets. Find which section the address falls into, and calculate how far into the section it is, then add the file offset. Now use a hex editor to fetch the 8 bytes representing your double from that offset. Turn it into human-readable form by whatever means you want, for example by a trivial C program that just casts a byte array to double and prints it.

Finding index of a substring in COBOL

I'm looking for the positions in a string where a specified substring occurs.
E.g, looking for substring "green" in the the string "green eggs and ham" should return me 1, but from "green eggs and green ham" would return me 1 and 14.
How should I do this?
Edit 1: Changed the wording so position starts at 1, not 0.
Edit 2: I can find the first instance as WS-POINTER in the following snippet:
MOVE 1 TO WS-POINTER
UNSTRING WS-STRING(1:WS-STRING-LEN)
DELIMITED BY LT-MY-DELIMITER
INTO WS-STRING-GARBAGE
WITH POINTER WS-POINTER
END-UNSTRING
AFAIK COBOL does not have a statement to find the position of a string within a string, so that needs to be done manually. However, COBOL does have a statement that counts the occurrences of a string within a string:
INSPECT string TALLYING counter FOR ALL search-string
Here is an example program that works in OpenCOBOL (see OpenCobol.org):
IDENTIFICATION DIVISION.
PROGRAM-ID. OCCURRENCES.
ENVIRONMENT DIVISION.
INPUT-OUTPUT SECTION.
FILE-CONTROL.
DATA DIVISION.
FILE SECTION.
WORKING-STORAGE SECTION.
01 TEST-STRING-1 PIC X(30)
VALUE 'green eggs and ham'.
01 TEST-STRING-2 PIC X(30)
VALUE 'green eggs and green ham'.
01 TEST-STRING PIC X(30).
01 SEARCH-STRING PIC X(05)
VALUE 'green'.
01 MATCH-COUNT PIC 9.
01 SEARCH-INDEX PIC 99.
01 MATCH-POSITIONS.
05 MATCH-POS PIC 99 OCCURS 9 TIMES.
PROCEDURE DIVISION.
MAIN.
MOVE TEST-STRING-1 TO TEST-STRING
PERFORM FIND-MATCHES
MOVE TEST-STRING-2 TO TEST-STRING
PERFORM FIND-MATCHES
STOP RUN
.
FIND-MATCHES.
MOVE ZERO TO MATCH-COUNT
INSPECT TEST-STRING TALLYING MATCH-COUNT
FOR ALL SEARCH-STRING.
DISPLAY 'FOUND ' MATCH-COUNT ' OCCURRENCE(S) OF '
SEARCH-STRING ' IN:'
DISPLAY TEST-STRING
DISPLAY 'MATCHES FOUND AT POSITIONS: ' WITH NO ADVANCING
PERFORM VARYING SEARCH-INDEX FROM 1 BY 1
UNTIL SEARCH-INDEX = 30
IF TEST-STRING (SEARCH-INDEX:5) = SEARCH-STRING
DISPLAY SEARCH-INDEX ' ' WITH NO ADVANCING
END-PERFORM
DISPLAY ' '
DISPLAY ' '
.
You could use QCLSCAN on IBM i
77 QCLSCAN-SRCHLEN PIC S9(3) COMP-3.
77 QCLSCAN-STARTPOS PIC S9(3) COMP-3.
77 QCLSCAN-PATLEN PIC S9(3) COMP-3.
77 QCLSCAN-XLATE PIC X(01) VALUE "0".
77 QCLSCAN-TRIM PIC X(01) VALUE "0".
77 QCLSCAN-WILDCARD PIC X(01) VALUE LOW-VALUES.
77 QCLSCAN-FOUNDPOS PIC S9(3) COMP-3.
...
...
MOVE LENGTH OF WRK-ACCT-NBR TO QCLSCAN-SRCHLEN
MOVE 1 TO QCLSCAN-STARTPOS
MOVE 9 TO QCLSCAN-PATLEN
MOVE "0" TO QCLSCAN-XLATE
MOVE "0" TO QCLSCAN-TRIM
MOVE "?" TO QCLSCAN-WILDCARD
CALL "QCLSCAN" USING WRK-ACCT-NBR
QCLSCAN-SRCHLEN
QCLSCAN-STARTPOS
EMPLOYEE-SSN-9X
QCLSCAN-PATLEN
QCLSCAN-XLATE
QCLSCAN-TRIM
QCLSCAN-WILDCARD
QCLSCAN-FOUNDPOS
IF QCLSCAN-FOUNDPOS > ZERO
* Found data in position QCLSCAN-FOUNDPOS
ELSE
* Found no match
END-IF
MOVE 1 TO WS-POINTER
UNSTRING WS-STRING(1:WS-STRING-LEN)
DELIMITED BY LT-MY-DELIMITER
INTO WS-STRING-GARBAGE
WITH POINTER WS-POINTER
END-UNSTRING
You ask about how to use the above for subsequent strings.
It is possible to use UNSTRING in two ways to get the counts you want. Either by having multiple receiving fields and COUNT-IN or by using multiple executions of UNSTRING using the POINTER value from the previous UNSTRING each time.
You need to account for the length of the delimiter. However, you will end up with "non-intuitive" code which will have to be "understood" each time someone picks up the program with it in.
Instead, it is a simple task with "substring" processing with either OCCURS DEPENDING ON or reference-modification (the method in the accepted answer).
You must make sure you don't "go beyond the end of the field" by ending the search when count + length-of-delimiter = max-length-of-string-to-search.

How can I convert Special Format String to Text?

How can I convert the string 00 00 EF 01 00 00 00 00 00 00 to text?
I googled and found a online tool which can convert binary to text only.
This values are in HEX - This tool
does hex as well, you can always transalte HEX to decimal and then take their ASCII value...
I created a tool few years ago that can convert/encode strings. Hope you'll find it useful.
I'm assuming here the text you've supplied is "as is", with spaces separating the hex digit pairs.
You can convert each hex value with, e.g.:
byte.Parse("EF", System.Globalization.NumberStyles.AllowHexSpecifier)
So you can convert the whole to a byte array:
var byteArray = "0A 0A 0A".Split(' ').Select(s => byte.Parse(s, System.Globalization.NumberStyles.AllowHexSpecifier)).ToArray();
However, you don't specify what character encoding your hex stream represents. Once you've got your byte array, you'll need to convert it as necessary.

Resources