Robert (and Karl-Heinz),
This is what I came up with, taking into consideration the remarks from Karl-Heinz. I tested the interoperability of these functions (STRTOFILE() and FILETOSTR()) with VFP and they seem to be working as expected.
A test can be performed against binary files by reading and then writing an image file, for instance:
Code: Select all
STRTOFILE(FILETOSTR("C:Tempimage.jpg"), "C:Tempcopy of image.jpg")
The proposed "final" implementation:
Code: Select all
* VFP standard flags
DEFINE S2F_FLAG_OVERWRITE = 0x0000
DEFINE S2F_FLAG_APPEND = 0x0001
DEFINE S2F_FLAG_UNICODE_LE = 0x0002
DEFINE S2F_FLAG_UTF8 = 0x0004
* X# extension flags
DEFINE S2F_FLAG_UNICODE_BE = 0x0008
DEFINE S2F_FLAG_UNICODE_FORMATS = S2F_FLAG_UNICODE_LE | S2F_FLAG_UTF8 | S2F_FLAG_UNICODE_BE
DEFINE S2F_FLAG_UNICODE_TEXT = 0x0100
FUNCTION StrToFile (Expression AS String, Filename AS String) AS Int
RETURN StrToFile(Expression, Filename, S2F_FLAG_OVERWRITE)
FUNCTION StrToFile (Expression AS String, Filename AS String, Additive AS Boolean) AS Int
RETURN StrToFile(Expression, Filename, IIF(Additive, S2F_FLAG_APPEND, S2F_FLAG_OVERWRITE))
FUNCTION StrToFile (Expression AS String, Filename AS String, Flags AS Int) AS Int
LOCAL Additive = .F. AS Boolean
LOCAL BOM = "" AS String
LOCAL Result = 0 AS Int
LOCAL FHandle AS Int
LOCAL VFPBehavior = .T. AS Boolean // it means the string must hold an already prepared buffer, or it is binary
LOCAL UnicodeEncoding AS System.Text.Encoding
LOCAL IOError AS Exception
DO CASE
* VFP behavior, append to a file with no Unicode encoding
CASE Flags = S2F_FLAG_APPEND
Additive = .T.
* VFP behavior, create a Unicode file (source buffer already prepared)
CASE Flags = S2F_FLAG_UNICODE_LE
BOM = e"xFFxFE"
* VFP behavior, create a UTF-8 file (source buffer already prepared)
CASE Flags = S2F_FLAG_UTF8
BOM = e"xEFxBBxBF"
* extension to VFP behavior, create a Unicode Big-Endian file (source buffer already prepared)
CASE Flags = S2F_FLAG_UNICODE_BE
BOM = e"xFExFF"
* if not simply overwrite, the file will be treated as a Unicode text file
CASE Flags != S2F_FLAG_OVERWRITE
IF (Flags & S2F_FLAG_UNICODE_TEXT) != 0
VFPBehavior = .F.
Additive = (Flags & S2F_FLAG_APPEND) != 0
* set the Unicode encoding
SWITCH Flags & S2F_FLAG_UNICODE_FORMATS
CASE S2F_FLAG_UNICODE_LE
UnicodeEncoding = System.Text.Encoding.Unicode
CASE S2F_FLAG_UTF8
UnicodeEncoding = System.Text.Encoding.UTF8
CASE S2F_FLAG_UNICODE_BE
UnicodeEncoding = System.Text.Encoding.BigEndianUnicode
* error if Unicode encoding not properly set
OTHERWISE
THROW ArgumentException {}
END SWITCH
ELSE
THROW ArgumentException {}
ENDIF
END CASE
* append mode?
IF Additive
IF VFPBehavior
* open an existing file, or create if it does not exists
FHandle = FOpen(Filename, FO_READWRITE + FO_SHARED)
IF FHandle != F_ERROR
* try to move to the end of the file
FSeek3(FHandle, 0, FS_END)
* and write the contents of the buffer
IF FError() == 0
Result = FWrite(FHandle, Expression)
ENDIF
* if everything went ok, close the file handle
IF FError() == 0
FClose(FHandle)
IF FError() != 0
THROW FException()
ENDIF
* if not, before throwing the exception...
ELSE
IOError = FException()
FClose(FHandle) // ... try to close the handle, anyway
THROW IOError
ENDIF
ELSE
THROW FException()
ENDIF
ELSE
* in non-VFP behavior, just append the Unicode string to an existing file
TRY
File.AppendAllText(Filename, Expression, UnicodeEncoding)
CATCH
THROW
ENDTRY
Result = Expression:Length
ENDIF
ELSE
* TO-DO: check on SET("Safety")
* create a new file
IF VFPBehavior
* get an handle for a new file
FHandle = FCreate(Filename)
IF FHandle != F_ERROR
* start with the BOM of the file
IF ! (BOM == "")
Result = FWrite(FHandle, BOM)
ENDIF
* try to write the contents from the buffer
IF FError() == 0
Result += FWrite(FHandle, Expression)
ENDIF
* and close the file
IF FError() == 0
FClose(FHandle)
IF FError() != 0
THROW FException()
ENDIF
* if an error occurred...
ELSE
IOError = FException()
FClose(FHandle) // ... try to not leave the handle open, in any case
THROW IOError
ENDIF
ELSE
THROW FException()
ENDIF
ELSE
* write the Unicode string to a text file
TRY
File.WriteAllText(Filename, Expression, UnicodeEncoding)
CATCH
THROW
ENDTRY
Result = Expression:Length
ENDIF
ENDIF
* return the length of bytes / characters
RETURN Result
ENDFUNC
FUNCTION FileToStr (Filename AS String) AS String
RETURN FileToStr(Filename, S2F_FLAG_OVERWRITE)
FUNCTION FileToStr (Filename AS String, Flags AS Int) AS String
LOCAL FHandle AS Int
LOCAL StrLen AS Int
LOCAL Result = "" AS String
LOCAL IOError AS Exception
IF (Flags & S2F_FLAG_UNICODE_TEXT) = 0 // VFP behavior, read file as binary, even if it is a Unicode text
* TO-DO - work with the SET("Path") setting to look for a file
FHandle = FOpen(Filename, FO_READ + FO_SHARED)
IF FHandle != F_ERROR
* success opening the file, get its size...
StrLen = FSize(FHandle)
IF StrLen > 0
* ...and allocate a buffer for it
Result = SPACE(StrLen)
IF FRead(FHandle, @Result, StrLen) != StrLen
Result = ""
ENDIF
ELSE
Result = ""
ENDIF
* any IO error will throw an exception
IF FError() != 0
IOError = FException()
FClose(FHandle)
THROW IOError
ELSE
FClose(FHandle)
ENDIF
ELSE
THROW FException()
ENDIF
ELSE
* for a Unicode text file, just read it into a string
TRY
Result = File.ReadAllText(Filename) // read a text file
CATCH
THROW
ENDTRY
ENDIF
* return the contents of the file
RETURN Result
ENDFUNC