pic x(100) field-a and
pic x(100) field-b Both left-justified & capitalized. Field-b-length is the position of its last non-blank byte.
I need to match to see if field-b (for a length of field-b-length) occurs anywhere in field-a, ie if field-b = 'DOG' and field-a = 'MANY PEOPLE LIKE DOGS BUT SOME PREFER CATS (followed by spaces to a length of 100)' it would match them.
I looked at the INSPECT statement but it matches for a literal value, and not for a variable name. Like I could do an Inspect field-a tallying cnt-1 for all 'DOG', but not INSPECT field-a tallying cnt-1 for all field-b (1 : field-b-length), which in this case = 'DOG'.
Field-a is in a file with 20 million records, and field-b is in a file with 400 entries, so the job is running 10+ hours, and I have to run 25+ of these jobs, so I have to check field-a against every entry in the field-b file (read into an array in the program).
Currently I'm determining the length of field-b and comparing it to field-a in bytes one through the length of field-b. If any byte doesn't match I quit, and shift field-a to the left one and try again until I have tried (100 - field-b-length) number of times. It's ugly but it works.
I was looking for something more efficient and elegant.
13 Answers
Which COBOL doesn't take identifier-3 as a tally criteria?
This should work
01 field-a pic x(100) value "MANY PEOPLE LIKE DOGS AND CATS AND MORE DOGS". 01 field-b pic x(100) value "DOG". 01 comparison-length pic 999 value 3. 01 found-count pic 999. 01 position-tally pic 999. ...
inspect field-a tallying found-count for all field-b(1:comparison-length) if found-count greater than zero then display "Found " found-count " occurrences" end-display else display "No " field-b(1:comparison-length) " found" end-display end-if move "HORSE" to field-b move zero to comparison-length inspect field-b tallying comparison-length for characters before initial space inspect field-a tallying position-tally for characters before initial field-b(1:comparison-length) if position-tally less than length of field-a then display "Found at " position-tally end-display else display "No " field-b(1:comparison-length) " found" end-display end-if giving
Found 002 occurrences No HORSE found But, this was tested with GnuCOBOL, so I'd be curious about your particular compiler.
Edit; code changed a little due to comments by Bill Woodger. Left in two styles of detecting the substring, just because
0Firstly, your data-name won't compile, at least to the Standard. Ignoring that you have ignored the level-number, the name of a field, where present, must follow the level-number. The the clauses of a data-definition can then be in any order you like.
Secondly, "ugly but works" doesn't work, you just haven't tested the limits. Put DOG in positions 98, 99 and 100, and then do (100 - 3) attempts to find DOG from position one. The first attempt will start at position one, the 97th attempt will start at position 97. The 98th byte will never be used as a starting point, so a trailing DOG will never match. Off-by-one. Off-by-frog. Off-by-DOG.
As Brian Tanner has said, INSPECT does do what you want. INSPECT has the advantage of knowing that your data, for a three-byte reference-modification, can start in position 98 and still get a match. The INSPECT is already coded for you.
Since you have not said you need the count of all DOGs, just to know it is there, then use FIRST, not ALL. The INSPECT will "stop" when a match is found. This will only affect the performance of "matches", but still, no need to just do extra for the heck of it.
Whether INSPECT is the fastest way to do it is down to the implementation in the actual compiler you use. So if desperate for speed (task required daily, five times a day) test it, and see how it does against your performance requirement.
If you need to look for more speed, you need to know the data well.
In IBM's Enterprise COBOL, INSPECT with TALLYING, ALL (can't use FIRST as I originally suggested, only available for REPLACING on INSPECT) and the reference-modified field is the fastest general-purpose way to do it, and only if you require more performance than that should you look elsewhere.
There are a number of "searching" tasks which INSPECT can be considered for, typically: is it there (your task); how many are there; where is it?
For the first two, INSPECT with TALLYING and ALL. Although it seems obtuse to count all of them if the only need is to know the presence or otherwise, this version of the INSPECT is the fastest way to do it. If it counts one, and then continues to the end still hunting, it only does that on records with a match. The alternate use of INSPECT, to count ALL CHARACTERS BEFORE..., is "very slow" (a relative term).
To fine the start position in the most efficient way, firstly INSPECT to see if there is something to look for. Then, use a loop to look for the data (which you will know is there, so you don't need to check for "over-run" of the field).
These references to the performance of INSPECT may not be true with your particular compiler, because it is entirely down to the implementation.
As always, with performance issues, the actual nature of the data will aid you in selecting the best way. The recommendations above hold (given compiler) with "normal-looking" data. If your data isn't particularly "normal", concentrating on what is not normal about it will assist.
Let's say that 90% of the 20 million records have data to be searched which is 30 bytes or less.
01 field-to-search. 05 mostly-data-here PIC X(30). 05 sometimes-data-here PIC X(70). 88 only-the-short-data VALUE SPACE. IF only-the-short-data INSPECT mostly-data-here ... ELSE INSPECT field-to-search ... END-IF Thus, 90% of the INSPECTs will only be on 30 bytes, at the cost of an IF to test for space using an 88. That's only one variation of what you can do - what is best depends on the data. Understand the data. Consider a couple of ways which will work. Test to confirm they work. Volume test with data which reflects reality. Also volume test with completely different data. Different solutions can be faster or slower, depending on the data.
4It sounds to me as though you should use a database. It would be worth paying the cost of loading both files into tables and then doing a SELECT ... WHERE with a LIKE clause. I would expect the whole process to run in under a couple of minutes.