The following issues have been identified with zstring data, but it can be assumed that this also occurs for wstring data.
2 test programs that highlight the bad behavior of 'PUT/GET (File I/O)' when putting/getting zstring data:
a) 'PUT (File I/O)' when putting zstring data:
#include "file.bi" Dim As Zstring * (15+1) zbuffer = "Hello FreeBASIC" '' 15 string character data Dim As Zstring Ptr pzbuffer = @zbuffer Open "test.bin" For Binary Access Write As #99 Print "returned by 'Put(#99, , zbuffer, 5)' (0 = success): " & Put(#99, , zbuffer, 5) Close #99 Print " file lenght: " & Filelen("test.bin") Print Open "test.bin" For Binary Access Write As #99 Print "returned by 'Put(#99, , zbuffer)' (0 = success): " & Put(#99, , zbuffer) Close #99 Print " file lenght: " & Filelen("test.bin") Print Open "test.bin" For Binary Access Write As #99 Print "returned by 'Put(#99, , *pzbuffer, 5)' (0 = success): " & Put(#99, , *pzbuffer, 5) Close #99 Print " file lenght: " & Filelen("test.bin") Print Open "test.bin" For Binary Access Write As #99 Print "returned by 'Put(#99, , *pzbuffer)' (0 = success): " & Put(#99, , *pzbuffer) Close #99 Print " file lenght: " & Filelen("test.bin") Print Open "test.bin" For Binary Access Write As #99 Print "returned by 'Put(#99, , zbuffer[0], 5)' (0 = success): " & Put(#99, , zbuffer[0], 5) Close #99 Print " file lenght: " & Filelen("test.bin") Print Open "test.bin" For Binary Access Write As #99 Print "returned by 'Put(#99, , (*pzbuffer)[0], 5)' (0 = success): " & Put(#99, , (*pzbuffer)[0], 5) Close #99 Print " file lenght: " & Filelen("test.bin") Print Sleep
b) 'GET (File I/O)' when getting zstring data:
(the most delicate in this last test program is to master the initial element values of the zstring buffer)
Open "test.bin" For Binary Access Write As #99 Put #99, , "Hello FreeBASIC" Close #99 Dim As Zstring * 33 zbuffer Dim As Zstring Ptr pzbuffer = @zbuffer Clear(zbuffer[0], 0, 33) Open "test.bin" For Binary Access Read As #99 Print "returned by 'get(#99, , zbuffer, 5)' (0 = success): " & get(#99, , zbuffer, 5), , "in zbuffer cleared" Close #99 Print " '" & Zbuffer & "'", "zbuffer length: " & Len(Zbuffer) Print Clear(zbuffer[0], 0, 33) Open "test.bin" For Binary Access Read As #99 Print "returned by 'get(#99, , zbuffer)' (0 = success): " & get(#99, , zbuffer), , "in zbuffer cleared" Close #99 Print " '" & Zbuffer & "'", "zbuffer length: " & Len(Zbuffer) Print Clear(zbuffer[0], 0, 33) Open "test.bin" For Binary Access Read As #99 Print "returned by 'Get(#99, , *pzbuffer, 5)' (0 = success): " & Get(#99, , *pzbuffer, 5),, "in zbuffer cleared" Close #99 Print " '" & Zbuffer & "'",, "zbuffer length: " & Len(Zbuffer) Print Clear(zbuffer[0], 0, 33) Open "test.bin" For Binary Access Read As #99 Print "returned by 'Get(#99, , *pzbuffer)' (0 = success): " & Get(#99, , *pzbuffer),, "in zbuffer cleared" Close #99 Print " '" & Zbuffer & "'",, "zbuffer length: " & Len(Zbuffer) Print Clear(zbuffer[0], 0, 33) : zbuffer = Space(5) Open "test.bin" For Binary Access Read As #99 Print "returned by 'Get(#99, , *pzbuffer)' (0 = success): " & Get(#99, , *pzbuffer),, "in zbuffer = Space(5)" Close #99 Print " '" & Zbuffer & "'",, "zbuffer length: " & Len(Zbuffer) Print Clear(zbuffer[0], 0, 33) : zbuffer = Space(10) Open "test.bin" For Binary Access Read As #99 Print "returned by 'Get(#99, , *pzbuffer)' (0 = success): " & Get(#99, , *pzbuffer),, "in zbuffer = Space(10)" Close #99 Print " '" & Zbuffer & "'", "zbuffer length: " & Len(Zbuffer) Print Clear(zbuffer[0], 0, 33) : zbuffer = Space(32) Open "test.bin" For Binary Access Read As #99 Print "returned by 'Get(#99, , *pzbuffer)' (0 = success): " & Get(#99, , *pzbuffer),, "in zbuffer = Space(32)" Close #99 Print " '" & Zbuffer & "'", "zbuffer length: " & Len(Zbuffer) Print Clear(zbuffer[0], 0, 33) Open "test.bin" For Binary Access Read As #99 Print "returned by 'Get(#99, , zbuffer[0]), 5' (0 = success): " & Get(#99, , zbuffer[0], 5), "in zbuffer cleared" Close #99 Print " '" & Zbuffer & "'",, "zbuffer length: " & Len(Zbuffer) Print Clear(zbuffer[0], 0, 33) Open "test.bin" For Binary Access Read As #99 Print "returned by 'Get(#99, , (*pzbuffer)[0], 5)' (0 = success): " & Get(#99, ,(*pzbuffer)[0], 5), "in zbuffer cleared" Close #99 Print " '" & zbuffer & "'",, "zbuffer length: " & Len(zbuffer) Print Sleep
a) 'PUT (file I/O)' behavior summary:
- When a real zstring variable (zbuffer
) is passed, the behavior should be the same than for a fix-len string (dim as string * N ...
). So the amount
parameter should be forbidden. Otherwise, it is dangerously used to multiply the string length to be written to the file, but possibly by overflowing outside the provided zstring buffer. => NOK
- When a dereferenced zstring pointer (*pzbuffer
) is passed, the amount
parameter is also authorized but seems to be ignored because the pointed buffer is written to the file up to the zero element (terminal element which is excluded). => NOK
- When an ubyte (zbuffer[0]
or (*pzbuffer)[0]
) is passed, the behavior is correct with the amount
parameter well used. => OK
b) 'GET (file I/O)' behavior summary:
- When a real zstring variable (zbuffer
) is passed, the behavior should be the same than for a fix-len string (dim as string * N ...
). So the amount
parameter should be forbidden. Instead, it is ignored (except for the '0' value). => NOK
- When a dereferenced zstring pointer (*pzbuffer
) is passed, the amount
parameter is also authorized but seems to be ignored. But to work, the pointed buffer must begin with at least as many non-zero elements as the number of elements to read. => NOK
- When an ubyte (zbuffer[0]
or (*pzbuffer)[0]
) is passed, the behavior is correct with the amount
parameter well used. => OK
See also discussion on forum ('Trouble with file I/O put command' topic in General)
https://www.freebasic.net/forum/viewtopic.php?f=3&t=29688
1) For 'PUT (File I/O)' and 'GET (File I/O)', the amount
parameter must be forbidden when any [w/z]string is passed (a real [w/z]string variable or a dereferenced [w/z]string pointer).
2) For 'GET (File I/O)' and a dereferenced [w/z]string pointer passed, the write process to the buffer must work whatever the initial element values of the pointed buffer (as it does for a real [w/z]string variable passed).
When the zstring buffer is dynamically allocated, only a 'zstring ptr' is directly available, so this bug is encountered when using 'GET (file I/O)':
Therefore for this bad 'GET (file I/O)' behavior, a workaround (other than initializing the values of the buffer elements) is to use an equivalent zstring structure in a Type, and place it to the address of the dynamically allocated memory:
Last edit: fxm (freebasic.net) 2021-11-08