Chapter 16. Additional Remarks when Reading Files

Direct Access to CMS files

We discuss now how one can access data in files directly or randomly instead of sequentially.

We have already told you that CMS can find each individual record of a file directly, thanks to is index structure.

How to request specific records directly depends on the File I/O method.  Let's first suppose that we want to read one single record.  The different I/O methods give then:

  /* Reading one specific record */
  address command
  parse value '75 MY FILE A' with lnum fileid

  /******* EXECIO *******/
       'EXECIO 1 DISKR' fileid lnum '( VAR RECORD'
  /******* XEDIT ********/
       queue ':' lnum
       queue 'COMMAND STACK 1'
       queue 'COMMAND QUIT'
       'XEDIT' fileid '(NOPROF'
  /******* LINEIN *******/
       record=linein(fileid,lnum,1)

Note: The file is not closed in the examples above, except with the XEDIT solution.

If we now look at how we can read 10 consecutive records starting from absolute record 75, then we have:

  /* Reading one specific record */
  address command
  parse value '75 10 MY FILE A' with lnum nrec fileid

  /******* EXECIO *******/
       'EXECIO' nrec 'DISKR' fileid lnum '(STEM RECORD.'
  /******* XEDIT ********/
       queue ':' lnum
       queue 'COMMAND STACK 10'
       queue 'COMMAND QUIT'
       'XEDIT' fileid '(NOPROF'
  /******* LINEIN *******/
       call stream fileid,'COMMAND','LINEPOS ='lnum 'READ' /* set read ptr */
       do i=1 to nrec
          record.i=linein(fileid)
       end

Another example where XEDIT can be handy is when you want to include part of one file into another:

   queue '/USER ABC/'
   queue 'COMMAND GET USER INCLUDES A 25 10'
   queue 'COMMAND FILE'
   'XEDIT SOME FILE A'

In this example, you edit the file SOME FILE A, position at the first record containing USER ABC and imbed records 25 to 34 (10 records) from file USER INCLUDES A.

Until now, we didn't include examples for CMS Pipelines here.  We will have examples later.


Direct access on Personal Systems.

We will not extend on the matter here, but note that it is also possible to directly access file records on Personel Systems.  The files need however to have a fixed record length structure, that is, each record must have the same number of characters.  Then, using the SEEK command of the STREAM() function, it is possible to extract a specific record directly.


Locating strings in files.

You will often need to process records of a file based on specific strings they contain.  It is of course possible to select the proper records with pure REXX coding (this is even the only way when you read the files using Stream I/O or CSL methods).

But, EXECIO, XEDIT and CMS Pipelines have again an added value in this case.  These commands have the possibility to extract records based on the presence of a string.  This results in some storage savings, and as the logic used by these commands is not interpreted but compiled, the process may be much faster than with REXX.

So, REXX has the greatest flexibility (you can select records with more precision, use arithmetic comparisions, etc.).  Next comes CMS Pipelines which has also plenty of selection filters.  XEDIT and EXECIO close the line with the least possibilities.

You should always keep in mind that I/O processing is the time consuming part of your programs.  So, for example, if you have to combine multiple selections, it will be beneficial for performance to keep the file in storage.  XEDIT and REXX(footnote) are the easiest in this case.  CMS Pipelines is less obvious for novice plumbers, while EXECIO does not allow you to process records from storage.

FIND or LOCATE ?

XEDIT, EXECIO and CMS Pipelines have both the LOCATE and FIND (sub)commands.  From experience, we know that the differences are not well understood, so let's expand a little bit :

LOCATE :

This subcommand will search for a given, delimited character string in that part of the records that is called the zone.  The string can be located anywhere inside the boundaries of the zone, which by default spans the complete record.

The different tools let you LOCATE(3) a string in a zone as follows :

XEDIT
[SET ZONE scol ecol]
LOCATE /string/
EXECIO
EXECIO nlines DISKR fileid ([ZONE scol ecol] LOCATE /string/
PIPE
PIPE ...!LOCATE [scol-ecol] /string/!...

Notes :

  1. XEDIT has possibilities for more complex and combined targets (AND, OR), and can also perform a case insensitive search.
  2. CMS Pipelines allows for multiple separate zones to be specified, while XEDIT and EXECIO allow only one zone.
  3. CMS Pipelines has also the ALL stage that is similar to XEDITs' ALL macro, and thus allows complex targets.  Case insensitive search is possible via the CASEI stage, or using the anycase option available to many selection stages since VM/ESA Version 2.
  4. The string delimiter has not to be the '/', any character not appearing in the search string will do.

FIND :

This subcommand looks for a given string that starts at the beginning of the record or at a given column.  As you can understand, this search needs less machine processing, so that you should prefer this function to LOCATE whenever possible.

The different command formats are :

XEDIT
[SET IMAGE ON]
[SET TABS scol]
FIND string
EXECIO
EXECIO nlines DISKR fileid ([ZONE scol ecol] FIND /string/
PIPE
PIPE ...!FIND string!...

Notes :

  1. XEDITs FIND starts looking in the first tab column when IMAGE is set ON, else it searches for the string at column 1 (see HELP XEDIT FIND, Usage notes).
  2. Both XEDIT and PIPE consider a blank in the search string a "matching any character" (wildcard).  EXECIO has not this possibility, as it uses a delimited string.  Suppose you have a VM Directory file (and USER records start at column 1), then a
          'FIND USER MAINT '
    

    will match both MAINT and MAINTSNA, while a

          'LOCATE /USER MAINT /'
    

    will only match MAINT !
    You have to specify an underscore (_) if you want to search for a blank.  So, in order to find MAINT only, you would use the command

          'FIND USER MAINT_'
    
  3. PIPE only searches from the beginning of the record, but it is of course possible to temporarily re-arrange the record layout prior to issuing the FIND stage, or, by using ZONE.

  4. For EXECIO, the only difference with LOCATE is that FIND limits the search to the start of the zone.

Extra notes for XEDIT.

  1. XEDIT has the advantage to have the whole file in storage.  Only if the file is too large for the available storage, you would have to revert to another method.
  2. It is possible to pass file records from XEDIT to REXX.  You can use the STACK subcommand (record length limited to 255 by the CMS stack), but EXTRACT /CURLINE/ is probably much easier, though it can only transfer one record at a time.

Extra notes for EXECIO.

  1. If we analyze the command format,
       EXECIO nlines DISKR fileid (LOCATE /string/
                  or
       EXECIO nlines DISKR fileid (FIND /string/
    

    then nlines does not specify how many records should be returned, but only how far EXECIO has to scan for the string.

  2. With EXECIO, when a matching file record is found, 2 lines are returned:
    1. the relative and absolute record position
    2. the matching record itself.

    For example, if the first record containing the string MAINT is the tenth record in the file, then a

       EXECIO * DISKR USER DIRECT A 4 (FIND /USER MAINT /
    

    will return following records:

       6 10
       USER MAINT PASSW ...
    

    The relative record position is 6 as we started to search from record 4, while the absolute record position is of course 10.

  3. If you have to find the next occurrence of a given string, then you have to continue to read with a linenum set to zero, or not to specify linenum at all.  EXECIO remembers the current record position.
       relrec=4
       do i=1 by 1
          'EXECIO * DISKR USER DIRECT A' relrec '(LOCATE /USER /'
          if rc<>0 then leave                               /* end of file */
          parse pull relrec absrec .
          parse pull rec.i
          relrec=0
       end
       rec.0=i-1
    

This is the end of the chapter.  Chapter 17 will discuss output operations.


Footnote:

  In this case, some I/O method brought the records into storage and assigned them to a REXX stem.
Back to text