A simple StringToFile() procedure

These days the easiest way to save the contents of a string to a file is to use StringTheory’s SaveFile() method.

But in case you need share a code sample with somebody who you are not sure owns StringTheory, here is a simple procedure you can paste in your code:

  MAP
StringToFile    PROCEDURE(STRING pStr,STRING pFileName)
  END

...

StringToFile        PROCEDURE(STRING pStr,STRING pFileName)
bufSize               EQUATE(32768)
dosFile               FILE,DRIVER('DOS'),CREATE
buf                     RECORD;STRING(bufSize).
                      END
pos                   LONG(1)
  CODE
  dosFile{PROP:Name} = pFileName
  CREATE(dosFile)
  IF ERRORCODE() THEN STOP(ERRORCODE()&' '&ERROR()&' '&ERRORFILE());RETURN.
  OPEN(dosFile)
  LOOP UNTIL pos > LEN(pStr)
    dosFile.Buf = pStr[ pos : LEN(pStr) ]
    ADD(dosFile,CHOOSE(pos + bufSize > LEN(pStr), |
      LEN(pStr) - pos + 1, |
      bufSize))
    pos += bufSize
  .
  CLOSE(dosfile)

I tried to make this code as short as possible without making it too cryptic. I wrote it as part of a very simple unit testing app I’m currently using to tests classes. You can find it here.

SystemStringClass.ToFile has very similar code and same disadvantages.

Hi Carlos,

Yeah as Mike points out the built-in Clarion SystemStringClass has many methods that work the same as StringTheory so easiest way would be…
!*** after Global Includes embed point ***
include(‘SystemString.inc’),once

!*** declare a SystemStringClass object ***
sysString SystemStringClass

CODE
            !*** load the sysString object with text (many ways to do this ***
	sysString.FromString('Hello World')
	sysString.ToFile(path() & '\Test.TXT')
2 Likes

Hi Graham & Mike

SystemStringClass was introduced with C9, but wasn’t available in my installation of C9.1PE. It was when I upgraded to C11EE. I had the idea it was EE only, but it looks like it’s now also included in PE.

Anyway, this code should work in even older versions.

Mike

It’s probably more efficient to use CreateFile/WriteFile API calls like you did in CJSON, but in this case I wanted to use only Clarion code, just a personal preference for the TestApp project.

Thanks to both for the replies, regards.

Carlos

Friends don’t let friends live without StringTheory… :slight_smile:

1 Like

LEN(pStr) is called 4 times every Loop. There is a cost to that so change to do it once. It’s not CLIP() so can use SIZE(pStr) to simply get the defined size while LEN() pushes a Copy of the String on the Stack then measures it.

I also moved the Slice End calculation out of the CHOOSE() in ADD() to a variable. That allows the slice assignment Buf=pStr[:] end to be shorter than LEN(pStr)

  lengthStr = SIZE(pStr)
  LOOP pos = 1 TO lengthStr BY bufSize
     endPos = pos + bufSize - 1
     IF endPos > lengthStr THEN endPos = lengthStr.
     dosFile.Buf = pStr[ pos : endPos ] 
     ADD(dosFile, endPos - pos + 1)
  END

I would change the return to (),BOOL,PROC so caller can optionally check ()= True/False for Success/Failure. Here’s the complete code. I have not tested it.

StringToFile          PROCEDURE  (STRING pStr,STRING pFileName) !,BOOL,PROC
bufSize               EQUATE(32768)
dosFile               FILE,DRIVER('DOS'),CREATE
                        RECORD,PRE()
buf                       STRING(bufSize)
                        END    
                      END
pos                   LONG,AUTO
endPos                LONG,AUTO
lengthStr             LONG,AUTO    !Instead of LEN(pStr) x 4
  CODE
  dosFile{PROP:Name} = pFileName
  CREATE(dosFile)
  IF ~ERRORCODE() THEN OPEN(dosFile).
  IF ERRORCODE() THEN 
     MESSAGE('Failed to Create and Open DosFile: '& pFileName &|
             '||Error: '& ERRORCODE()&' '&ERROR()&'|File: '&ERRORFILE(),'StringToFile')
     RETURN FALSE
  END 
  lengthStr = SIZE(pStr)        !or =LEN(CLIP(pStr)) removes trailing spaces
  LOOP pos = 1 TO lengthStr BY bufSize
     endPos = pos + bufSize - 1
     IF endPos > lengthStr THEN endPos = lengthStr.
     dosFile.Buf = pStr[ pos : endPos ]   
     ADD(dosFile, endPos - pos + 1)
  END
  CLOSE(dosfile) 
  RETURN True   
  !This code has NOT been tested at all ... This code has NOT been tested at all

Will this run measurably faster … probably not because memory operations, like LEN(), are so fast. Plus this is likely called with small strings and not 100’s of megabytes. I just like squeezing the drop, pretty soon your glass is only half empty.

2 Likes