Reputation: 3451
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
Upvotes: 3
Views: 23930
Reputation: 13076
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.
Upvotes: 0
Reputation: 1169
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
Upvotes: 0
Reputation: 46
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 ' '
.
Upvotes: 3