Welcome to the SRP Forum! Please refer to the SRP Forum FAQ post if you have any questions regarding how the forum works.
Append to an OS file in basic+?
in OpenInsight
Does Basic+ have a built-in way to append data to an OS file, something akin to opening a file in "a" mode in Python, or even >> in the Bourne shell?
While one can read the contents of the file into a variable with OSRead, append to the variable and then write the value back to the file with OSWrite, I notice that OSBWrite could almost be used to append. The documentation mentions that with byte equal to 0, writing starts at the beginning of the file. It would be handy if some other special value for byte would start writing at the end of the file, but the documentation doesn't go so far to say, and I haven't found that special value. Has anyone by some remote chance discovered an undocumented append feature of OSBWrite?
While one can read the contents of the file into a variable with OSRead, append to the variable and then write the value back to the file with OSWrite, I notice that OSBWrite could almost be used to append. The documentation mentions that with byte equal to 0, writing starts at the beginning of the file. It would be handy if some other special value for byte would start writing at the end of the file, but the documentation doesn't go so far to say, and I haven't found that special value. Has anyone by some remote chance discovered an undocumented append feature of OSBWrite?
Comments
I was trying to think of a reason/use case why you wouldn't just use the OSRead/Write approach you mentioned and the only thing I could think of was that you want to do multiple appends within a loop, to a file which likely already has data before the first append. I assume something like a log file that doesn't get cleared automatically.
If I'm on the right track, then you could use a combination of OSRead and then OSBWrite.
Upon initial opening of the file, OSRead the contents into a variable, use the GetByteSize Function to calculate the number of bytes and then begin your OSBWrites from there instead of zero.
When logging to OS I normally just do something quick and easy like:
declare function Get_File_Format $Insert APP_INSERTS ; // To get CRLF$ and TAB$ SaveTextFile = 1 ; // Normally set at Top of Procedure as a toggle when debugging or through parameter. // This allows many log entries in a script to be easily enabled/disabled. If SaveTextFile = 1 Then FileFormat = Get_File_Format() Dos.File = FileFormat:"_My_Log_File.txt" ; // Change this as required. TimeStamp = TimeDate() // Make output nicer to read. Method_Fmt = fmt(Method,"L#25") URL_Fmt = fmt(URL,"L#50") HeaderList_Fmt = fmt(HeaderList,"L#25") Body_Fmt = fmt(Body,"L#25") // Build Output text. Adjust as needed. MessageText = TimeStamp : CRLF$ : "METHOD:" : Method_Fmt : CRLF$ : "URL:" : URL_Fmt : CRLF$ MessageText := "HEADER:" : HeaderList_Fmt : CRLF$ : "BODY:" : Body_Fmt : CRLF$ : "TOKEN:" MessageText := OAuthAccessToken@ //Get rid of MultiValue delimiters from variables. Adj as needed Swap @VM with TAB$ in MessageText Swap @FM with CRLF$ in MessageText Swap @TM with CRLF$ in MessageText // If exists, Append. OSRead CurrentData From Dos.File Then CurrentData = CurrentData:CRLF$:"______________________________":CRLF$:CRLF$:MessageText End Else CurrentData = MessageText End OSWrite CurrentData To Dos.File end
Get_File_Format is just a little procedure to get a path and start a datestamp as the filename prefix like:
C:\MyLogs\230124
so my file ends up a consistent structure:
C:\MyLogs\230124_My_Log_File.txt
This keeps me a running log daily that appends the info I am after and I can look up historically if I have those logs automated.
I am always interested in better ways.
I don't know how performant the Dir() function is, but I'm fairly convinced it relies upon a Windows API to return the data thus saving the need to read the content. Therefore, I would expect it to perform favorably and uniformly regardless of the size of the file.
To rudimentarily reiterate what you and @AusMarkB indicated I could replace my OSREAD/OSWRITE with something like:
OSOpen FullyQualifiedFileName To filevar then CurrByteMark = Dir(FullyQualifiedFileName)<1> OSBWrite LogMessage to filevar at CurrByteMark + 1 End
And this should be more beneficial the more the log grows...
OSOpen FullyQualifiedFileName To filevar then
CurrByteMark = Dir(FullyQualifiedFileName)<1>
OSBWrite LogMessage to filevar at CurrByteMark + 1
End
but you'd also be correct in wondering at what point that would become more efficient and therefore what's the benefit?
I was more suggesting a scenario like
// closing application OSOpen FullyQualifiedFileName to filevar then CurrByteMark = Dir(FullyQualifiedFileName)<1> theSelect = "SELECT SOMETABLE WITH DATE = ":Quote(Oconv(today, "DE")) RList(theSelect, 5, "", "", "") eof = false4 Loop ReadNext tableID else eof = true$ Until eof do Read logRec from f_someTable, tableID then dosText = logRec<1>:" ":logRec<2>:" at ":logrec<4>:crlf$ dosBytes = GetByteSize(dosText) OSBWrite dosText to filevar at CurrByteMark CurrByteMark += dosBytes end Repeat end
or perhaps a background function that does something similar, keeps the filevar and currbytemark in common and writes a row away each time a specific event or various events occur.
For a one off write, I'd do the same as you, read, append to variable, write back.
Good options for everyone!
Wiki documentation and everything!
I knew that as I have seen that in SRP code but for some reason I keep forgetting some of the helpful things I come accross...
LOL. I totally forgot that this is included with the HTTP Framework and thus documented in the wiki!
I feel a little better now ;-)