Set EOF

New to FreeBASIC? Post your questions here.
jj2007
Posts: 2326
Joined: Oct 23, 2016 15:28
Location: Roma, Italia
Contact:

Re: Set EOF

Post by jj2007 »

fxm wrote:Opening a file in Binary (or Random) mode and also in Write (only) access mode allows you to first delete any previous record in the file (the file becomes empty before any writing operation).
Are you sure? Little demo?

@adele: Your code throws an error when trying to build it.
fxm
Moderator
Posts: 12107
Joined: Apr 22, 2009 12:46
Location: Paris suburbs, FRANCE

Re: Set EOF

Post by fxm »

jj2007 wrote:
fxm wrote:Opening a file in Binary (or Random) mode and also in Write (only) access mode allows you to first delete any previous record in the file (the file becomes empty before any writing operation).
Are you sure? Little demo?

Code: Select all

Dim buffer As String, f As Integer

buffer = "ABCDEFGHIJ"
f = FreeFile
Open "filetest.data" For Binary As #f
Put #f, , buffer
Close #f

Open "filetest.data" For Binary As #f
Print LOF(f)
Close #f

Open "filetest.data" For Binary Access Read As #f
Print LOF(f)
Close #f

Open "filetest.data" For Binary Access Write As #f
Print LOF(f)
Close #f

Sleep

Code: Select all

 10
 10
 0
jj2007
Posts: 2326
Joined: Oct 23, 2016 15:28
Location: Roma, Italia
Contact:

Re: Set EOF

Post by jj2007 »

Well, that is interesting - and confusing! So the choices here are...

Code: Select all

Open "filetest.data" For Binary As #f
Open "filetest.data" For Binary Access As #f
Open "filetest.data" For Binary Access Write As #f
... and only the last one truncates. IMHO the doc could be a bit more specific about this:
The Binary and Random file modes open disk files for random-access reading or writing of arbitrary sized binary data:

The Binary file mode allows reading and writing of simple data type values, like Byte or Longint, using binary read or write operations, like Get #. Loc and Seek are among the procedures used for reading and writing to arbitrary locations in the disk file.
The Random file mode is similar to Binary, except that file I/O operations always use a constant data size when reading or writing.

By default, the Binary and Random file modes allow both reading and writing operations on the opened disk file, but this can be changed by specifying an access type (see the description for the access_type parameter above).
fxm
Moderator
Posts: 12107
Joined: Apr 22, 2009 12:46
Location: Paris suburbs, FRANCE

Re: Set EOF

Post by fxm »

Proposed adding to the OPEN page in documentation:
.....
By default, the Binary and Random file modes allow both reading and writing operations on the opened disk file, but this can be changed by specifying an access type (see the description for the access_type parameter above). When opening an existing file in Binary or Random file mode, and with the only Write access type specified, all data in the file are previously deleted at opening, before any other operation.
.....
[edit] (Mar 12, 2018)
Done:
KeyPgOpen → fxm [Added the behavior on existing file opened in Binary or Random file mode, and with the only Write access type specified.]
Last edited by fxm on Mar 12, 2018 5:56, edited 1 time in total.
dodicat
Posts: 7983
Joined: Jan 10, 2006 20:30
Location: Scotland

Re: Set EOF

Post by dodicat »

Wouldn't it be easy enough to use the bog standard binary way of loading and saving text files and using the actual strings to change any content.

Code: Select all

 

'standard load and save text files:
 #Include "file.bi"
Sub savefile(filename As String,p As String)
    Dim As Integer n
    n=Freefile
    If Open (filename For Binary Access Write As #n)=0 Then
        Put #n,,p
        Close
    Else
        Print "Unable to save " + filename
    End If
End Sub

Function loadfile(file as string) as String
	If FileExists(file)=0 Then Print file;" not found":Sleep:end
   var  f=freefile
    Open file For Binary Access Read As #f
    Dim As String text
    If Lof(f) > 0  Then
      text = String(Lof(f), 0)
      Get #f, , text
    End If
    Close #f
    return text
end Function


'================   example  ==============

dim as string path=curdir+ mid(command(0),instrrev(command(0),"\"))
mid(path,len(path)-3)=".bas"

dim as string g=loadfile( path)
print g 'or any part of g


sleep

 
jj2007
Posts: 2326
Joined: Oct 23, 2016 15:28
Location: Roma, Italia
Contact:

Re: Set EOF

Post by jj2007 »

@fxm: Perfect.

@dodicat: I am not quite sure if I understand your intentions correctly (and you are not using savefile in your example). Does it more or less mean, as in the para below, "load the whole file into a buffer, change what you need, write the buffer back"?

I must admit that I find the debate a bit academic. In my practical work,
- with text files I use Open with output mode (so it truncates) or in append mode (and it doesn't, obviously)
- with data files I use Open in binary or random mode to change existing data, which doesn't and shouldn't truncate.
jj2007 wrote:
Juergen Kuehlwein wrote:I already have a file with let´s say 100 bytes of data. Now i change it´s content by writing 50 bytes of new data starting right at the beginning of the file opened in binary mode. This would leave 50 bytes of old data in my file. How can i cut off after 50 bytes, so that subsequent reads would only retrieve the new 50 bytes ?
There are situations where you don't start at the beginning of the file, but rather somewhere in the middle. If you write 50 bytes from position 20, and want to see 70 bytes and not more after closing the file, the solution (in the absence of SetEof) would be to read the first 20 bytes into a buffer, then open the file for output, print the 20 bytes, print the 50 bytes, close the file. Clumsy but it works.
grindstone
Posts: 862
Joined: May 05, 2015 5:35
Location: Germany

Re: Set EOF

Post by grindstone »

"load the whole file into a buffer, change what you need, write the buffer back"
That's the way of proceeding I would recommend anyhow, if there's no serious reason speaking against it.
dodicat
Posts: 7983
Joined: Jan 10, 2006 20:30
Location: Scotland

Re: Set EOF

Post by dodicat »

jj2007
loadfile and savefile do just that.
(I did not use savefile in my code)
buffer is confusing to me.
Load a text file into a freebasic var length string, tweak the string as you like and save the string as a text file.
For unicode work or arrays I would use a different method.
deltarho[1859]
Posts: 4310
Joined: Jan 02, 2017 0:34
Location: UK
Contact:

Re: Set EOF

Post by deltarho[1859] »

Spotted something whilst looking for something else and then came up with this.

Code: Select all

Sub SetEOF( filename As Zstring, buffer As String )
  Dim As handle hFile = CreateFile( @filename, GENERIC_WRITE, 0, 0, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0 )
  SetFilePointer( hFile, Len( buffer ), 0, FILE_BEGIN )
  SetEndOfFile( hFile )
  CloseHandle( hFile )
End Sub
However, to satisfy the opening post, Binary Access Write and then Putting buffer is a heck of a lot simpler. The API that I 'tripped' over was SetEndOfFile.
jj2007
Posts: 2326
Joined: Oct 23, 2016 15:28
Location: Roma, Italia
Contact:

Re: Set EOF

Post by jj2007 »

Wow, so simple! And I've never seen that function before...
Usually, the system takes care of setting the end of a file when the file is closed. However, you might sometimes want to make a file smaller or larger. On those occasions, call:

BOOL SetEndOfFile(HANDLE hFile);

This SetEndOfFile function changes the length of a file such that the value indicated by the file pointer becomes the length of the file. For example, if you wanted to force a file to be 1024 bytes long, you'd use SetEndOfFile this way:

HFILE hFile = CreateFile(...);
SetFilePointer(hFile, 1024, NULL, FILE_BEGIN);
SetEndOfFile(hFile);
CloseHandle(hFile);

If you use the File Manager to examine the directory containing this file, you'll see that the file is exactly 1024 bytes long.
Microsoft Developer Network Library, April 1996
caseih
Posts: 2157
Joined: Feb 26, 2007 5:32

Re: Set EOF

Post by caseih »

If I read the docs correctly, and if your file system is NTFS, moving the file pointer and using SetEndOfFile() will create a sparse file, which can be pretty useful.
Kwabbernoot
Posts: 80
Joined: Apr 19, 2010 18:23
Location: NL

Re: Set EOF

Post by Kwabbernoot »

Very useful information.
I had exactly the same problem, after rewriting a file, there was still a leftover of old data at the end of the file. I have a serie of programs and each program updates its record in a session logfile. The file is just a simple textfile. I read the whole file into a buffer and do the update in the buffer. I made the following change: if the new updated buffer is smaller than the old buffer then I close the file and reopen it with WRITE access so that the file is truncated/emptied and then I write the buffer back to the file.

Code: Select all

   DIM AS INTEGER Sess, OldBufLen
   DIM AS INTEGER Start, Endpos, Res
   DIM AS STRING  SesRecord, SesBuffer

'*** Open sessionlog

   Sess = FREEFILE
   OPEN "SESSLOG.TXT" FOR OUTPUT AS Sess
   PRINT #Sess, "20180320 ProgA ABCD"
   PRINT #Sess, "20180320 ProgB EFGHI"
   PRINT #Sess, "20180320 ProgC JKLMNOPQRS"
   PRINT "Lof: "; LOF(Sess)
   CLOSE Sess

'*** Read whole file into buffer

   OPEN "SESSLOG.TXT" FOR BINARY AS Sess

   OldBufLen = LOF(Sess)
   SesBuffer = SPACE(OldBufLen)
   GET #Sess, 1, SesBuffer
   PRINT "Buffer length: "; LEN(SesBuffer)
   PRINT "Lof          : "; LOF(Sess)

'*** Look for ProgB record

   Start = INSTR(SesBuffer, "ProgB")
   IF Start = 0 THEN END
            '*** Look for carriage return
   Endpos = INSTR(Start, SesBuffer, CHR(13))
   Start -= 10
   IF Endpos THEN
      IF Endpos < OldBufLen THEN
         IF SesBuffer[Endpos] = 10 THEN Endpos += 1
      END IF
      Endpos += 1
   ELSE
      PRINT "Error"
      END
   END IF

'*** Construct buffer

   SesRecord = "20180321 ProgB XX" + CHR(13, 10)
   SesBuffer = LEFT(SesBuffer, Start) + SesRecord + MID(SesBuffer,  Endpos)
   IF LEN(SesBuffer) < OldBufLen THEN

'*** New filesize is smaller so truncate file

      PRINT "Smaller"
      CLOSE Sess
      OPEN "SESSLOG.TXT" FOR BINARY ACCESS WRITE AS Sess
   END IF

'*** Write sessionlog

   PUT #Sess, 1, SesBuffer
   PRINT "Buffer length: "; LEN(SesBuffer)
   PRINT "Lof          : "; LOF(Sess)
   CLOSE Sess
   Res = SHELL("TYPE SESSLOG.TXT")
   SLEEP
Post Reply