The simplest snippet to read from file in InterSystems Caché


I believe the simplest is (to work with csv delimited by ";"):

set file = ##class(%File).%New( "data.csv" )
    set sc = file.Open( "R" ) 
    if $$$ISERR(sc) quit    ; or do smth

    while 'file.AtEnd {
        set str=file.ReadLine() 
        for i=1:1:$length( str, ";" ) {
            set id=$piece( str, ";" ,i ) 
            write !, id  // or do smth
    do file.Close()

Possible options:

different variants of error handling with sc code.

Embrace while loop into try/catch block.

And what's yours?


Ok, here is the "simplest"

N IO,D,P,I,A,X,L S IO=$I R D U 0 W !,D,! S P="^" F  U IO R A Q:A=""  I $P(A,P,3) S L=$P(A,P,5) S:L X=$ZU(55,L) ZL  ZS @$P(A,P) S:L X=$ZU(55,0) U 0 W !,$P(A,P,1,2),?20," loaded" ;(Self-loading)


(Well, I'm joking - this is the standard preamble for self-loading INT file)

I would generally advise against using %File class for reading files as this is a very low level wrapper around the COS file commands. For example if you want to apply a translate table you need to know about the 'K' flag on the open command. I much prefer using the %Stream.FileBinary or %Stream.FileCharacter classes as these provide a much more consistent interface to the file.


Thank you for sharing this snippet.

However, I don't understand why you de-piece a line with a delimiter of ";"?

I would just like to see the line like it is.

What am I missing here?

You have missed the extension of file "data.csv" used in the example. CSV stands for "Comma Separated Values", which is simplest way to exportt Excel like data to the textual file. And in this case it was apparently exported in the Russian-loale where "comma" is ";" actually.

Yes) Timur already answered. This snippet is not very general "read from file" snippet - but snippet to parse "russian-like" csvs) But every time when I work with text files line by line I use it. 


Thank you for that clarification, but, should that be part of the opening documentation?

Didn't get it. You mean I should change the description for the snippet?

Or to change the snippet to make it less "csv" specific?

Oh, thanks for this! I think it worth to make a separate snippet posting how to use the record mapper. Haven't found it in the documentation.

Sorry, I am not criticizing what you have done.

I had to ask why you de-pieced the line based on ";"

and I was given an answer.

Suppose I did not ask, took your snippet and assumed to worked for all files?

All I am saying is the assumption you used (Russian style files) should be part of your documentation so someone not familiar with these style of files would know. Any (unfamiliar) assumptions should be documented. 

Agreed. Will fix it. Anyway, you are very welcome to add your version of the simplest ever file management snippet ;)


Thanks for the tip on the Record Mapper, great stuff!

My code is designed to cope with field values that include commas, so it's not the simplest, but anyway:

CSVreaderMinimal(filename) ;Generic CSV reader. By Robert A Steed, 20/8/2018 (based on Import726).
 ;Assumes first line of file is a header containing field names.
 if $get(filename)="" write "No file name specified.",! quit
 new stream,sc,data,header,fldn,pn,piece,nof
 set stream=##class(%Stream.FileCharacter).%New()
 set sc=stream.LinkToFile(filename) ;Open a text file
 if sc'=1 write "Couldn't open ",filename,! do $System.Status.DisplayError(sc) quit
 set header=stream.ReadLine() ;Read header
 if sc'=1 write "Error.",filename,! do $System.Status.DisplayError(sc) quit
 set nof=$length(header,",") ;No. of fields
 while 'stream.AtEnd                                ;Loop through file
 {  set data=stream.ReadLine() ;Read a line from CSV file
    if sc'=1 write "Error.",! do $System.Status.DisplayError(sc) quit
    set fldn=0,pn=0 ;Field number, Piece number
    for fldn=1:1:nof
    {   set pn=pn+1
        set piece=$piece(data,",",pn)
        ;Deal with "-qualified data (optionally) containing commas by concatenating subsequent pieces until closing "
        if $extract(piece)=""""                         ;If piece starts with "
        {   do
            {   set pn=pn+1 ;Go to next piece
                set piece=piece_","_$piece(data,",",pn) ;Concatenate it to the previous
            while $extract(piece,$length(piece))'=""""    ;Until we get the one with " at the end
        write $J(fldn,2)," ",$J($piece(header,",",fldn),25),": ",piece,! ;Display field name and value
    write !