Text Files

Erel

Administrator
Staff member
Licensed User
Many applications use text files to store textual data, store user settings or many other usages.
It is pretty simple to work with text files, but still there are some points that you should be aware of.

The default encoding for text files is UTF8. This allows you to read or write any Unicode character.
You could choose to use ASCII encoding instead. ASCII encoding supports only the lower values of the ASCII table (0-127).
Note that UTF8 encoded files will start with a special Unicode marking.
Most applications recognize this marking and do not show it.
However, some older applications will show something like ";fe" at the beginning of the text. In such cases use ASCII encoding instead.
If you write a DOS batch file (on the desktop) then you should use ASCII encoding for that reason.

As an example we will create a small application with some user data. The user data will be loaded when the application starts (Sub App_Start) and saved when the application ends (Sub Form1_Close).
The source code file and the settings file are attached to the post.



Saving code:
B4X:
[SIZE=2][COLOR=#0000ff]Sub [/COLOR][/SIZE][SIZE=2]SaveINI
[/SIZE][SIZE=2][COLOR=#0000ff]  ErrorLabel[/COLOR][/SIZE][SIZE=2](errSaveINI)
[/SIZE][SIZE=2][COLOR=#0000ff]  FileOpen[/COLOR][/SIZE][SIZE=2]( c1,[/SIZE][SIZE=2][COLOR=#800000]"TextFiles.ini"[/COLOR][/SIZE][SIZE=2],cWrite)
[/SIZE][SIZE=2][COLOR=#0000ff]  FileWrite[/COLOR][/SIZE][SIZE=2](c1,txtFirstName.Text)
[/SIZE][SIZE=2][COLOR=#0000ff]  FileWrite[/COLOR][/SIZE][SIZE=2](c1,txtLastName.Text)
[/SIZE][SIZE=2][COLOR=#0000ff]  FileWrite[/COLOR][/SIZE][SIZE=2](c1,chkValidAccount.Checked) [/SIZE][SIZE=2][COLOR=#008000]'writing a boolean value.
[/COLOR][/SIZE][SIZE=2][COLOR=#0000ff]  For[/COLOR][/SIZE][SIZE=2] i = [/SIZE][SIZE=2][COLOR=#800080]0[/COLOR][/SIZE][SIZE=2][COLOR=#0000ff]To[/COLOR][/SIZE][SIZE=2] lstData.Count-[/SIZE][SIZE=2][COLOR=#800080]1
[/COLOR][/SIZE][SIZE=2][COLOR=#0000ff]    FileWrite[/COLOR][/SIZE][SIZE=2](c1,lstData.Item(i))
[/SIZE][SIZE=2][COLOR=#0000ff]  Next
[/COLOR][/SIZE][SIZE=2][COLOR=#0000ff]  FileClose[/COLOR][/SIZE][SIZE=2](c1)
[/SIZE][SIZE=2][COLOR=#0000ff]  Return[/COLOR][/SIZE][SIZE=2][COLOR=#008000]'If there were no "Return" here, the error message would have shown. 
[/COLOR][/SIZE][SIZE=2]  errSaveINI:
[/SIZE][SIZE=2][COLOR=#0000ff]  Msgbox[/COLOR][/SIZE][SIZE=2]([/SIZE][SIZE=2][COLOR=#800000]"Error writing INI file."[/COLOR][/SIZE][SIZE=2],[/SIZE][SIZE=2][COLOR=#800000]""[/COLOR][/SIZE][SIZE=2],cMsgboxOK,cMsgboxHand)
[/SIZE][SIZE=2][COLOR=#0000ff]  FileClose[/COLOR][/SIZE][SIZE=2](c1)[/SIZE]
[SIZE=2][COLOR=#0000ff]End Sub
[/COLOR][/SIZE]
File operations could fail, if for example the file is used by another application.
We are using ErrorLabel to handle unexpected errors and show a message to the user.

Now for the real part...
First we open the file with FileOpen.
The first parameter is the name that we give to this connection.
The second parameter is the path and file name.
In this case the file is located in the same folder of the source code (or compiled executable).
The third parameter could be cRead, cWrite or cRandom.
Text files should be opened for reading (cRead) or writing (cWrite), only binary files should use cRandom.
When using cWrite or cRandom, FileOpen will create a new file if it doesn't exist.
Text files are usually written one line after another and reading is done in a similar way.
FileWrite writes a string to the file, each string in a new line.
FileWrite receives two parameters, the connection name and the value to write.
Using FileWrite we store the form's data to the file.
As we've done writing we now close the connection with FileClose.
Only one connection could be made to a file, so it is important to close the connection when it is no longer required.

Now for the reading...
B4X:
[FONT=Courier New][SIZE=2][COLOR=#0000ff]Sub [/COLOR][/SIZE][/FONT][FONT=Courier New][SIZE=2]LoadINI[/SIZE][/FONT]
[FONT=Courier New][SIZE=2][COLOR=#0000ff]  ErrorLabel[/COLOR][/SIZE][/FONT][FONT=Courier New][SIZE=2](errLoadINI)[/SIZE][/FONT]
[FONT=Courier New][SIZE=2][COLOR=#0000ff]  If [/COLOR][/SIZE][/FONT][FONT=Courier New][SIZE=2][COLOR=#0000ff]Not[/COLOR][/SIZE][/FONT][FONT=Courier New][SIZE=2]([/SIZE][/FONT][FONT=Courier New][SIZE=2][COLOR=#0000ff]FileExist[/COLOR][/SIZE][/FONT][FONT=Courier New][SIZE=2]([/SIZE][/FONT][FONT=Courier New][SIZE=2][COLOR=#800000]"TextFiles.ini"[/COLOR][/SIZE][/FONT][FONT=Courier New][SIZE=2])) [/SIZE][/FONT][FONT=Courier New][SIZE=2][COLOR=#0000ff]Then [/COLOR][/SIZE][/FONT][FONT=Courier New][SIZE=2][COLOR=#0000ff]Return[/COLOR][/SIZE][/FONT]
[FONT=Courier New][SIZE=2][COLOR=#0000ff]  FileOpen[/COLOR][/SIZE][/FONT][FONT=Courier New][SIZE=2](c2,[/SIZE][/FONT][FONT=Courier New][SIZE=2][COLOR=#800000]"TextFiles.ini"[/COLOR][/SIZE][/FONT][FONT=Courier New][SIZE=2],cRead)[/SIZE][/FONT]
[SIZE=2][FONT=Courier New]  txtFirstName.Text = [/FONT][/SIZE][FONT=Courier New][SIZE=2][COLOR=#0000ff]FileRead[/COLOR][/SIZE][/FONT][FONT=Courier New][SIZE=2](c2)[/SIZE][/FONT]
[SIZE=2][FONT=Courier New]  txtLastName.Text = [/FONT][/SIZE][FONT=Courier New][SIZE=2][COLOR=#0000ff]FileRead[/COLOR][/SIZE][/FONT][FONT=Courier New][SIZE=2](c2)[/SIZE][/FONT]
[SIZE=2][FONT=Courier New]  chkValidAccount.Checked = [/FONT][/SIZE][FONT=Courier New][SIZE=2][COLOR=#0000ff]FileRead[/COLOR][/SIZE][/FONT][FONT=Courier New][SIZE=2](c2)[/SIZE][/FONT]
[SIZE=2][FONT=Courier New]  s = [/FONT][/SIZE][FONT=Courier New][SIZE=2][COLOR=#0000ff]FileRead[/COLOR][/SIZE][/FONT][FONT=Courier New][SIZE=2](c2)[/SIZE][/FONT]
[FONT=Courier New][SIZE=2][COLOR=#0000ff]  Do[/COLOR][/SIZE][/FONT][FONT=Courier New][SIZE=2][COLOR=#0000ff]Until[/COLOR][/SIZE][/FONT][FONT=Courier New][SIZE=2] s = EOF [/SIZE][/FONT][FONT=Courier New][SIZE=2][COLOR=#008000]'Read the ListBox items.[/COLOR][/SIZE][/FONT]
[FONT=Courier New][SIZE=2]   lstData.Add(s)[/SIZE][/FONT]
[SIZE=2][FONT=Courier New]   s = [/FONT][/SIZE][FONT=Courier New][SIZE=2][COLOR=#0000ff]FileRead[/COLOR][/SIZE][/FONT][FONT=Courier New][SIZE=2](c2)[/SIZE][/FONT]
[FONT=Courier New][SIZE=2][COLOR=#0000ff]  Loop[/COLOR][/SIZE][/FONT]
[FONT=Courier New][SIZE=2][COLOR=#0000ff]  FileClose[/COLOR][/SIZE][/FONT][FONT=Courier New][SIZE=2](c2)[/SIZE][/FONT]
[FONT=Courier New][SIZE=2][COLOR=#0000ff]  Return[/COLOR][/SIZE][/FONT]
[FONT=Courier New][SIZE=2]  errLoadINI:[/SIZE][/FONT]
[FONT=Courier New][SIZE=2][COLOR=#0000ff]  Msgbox[/COLOR][/SIZE][/FONT][FONT=Courier New][SIZE=2]([/SIZE][/FONT][FONT=Courier New][SIZE=2][COLOR=#800000]"Error reading INI file."[/COLOR][/SIZE][/FONT][FONT=Courier New][SIZE=2],[/SIZE][/FONT][FONT=Courier New][SIZE=2][COLOR=#800000]""[/COLOR][/SIZE][/FONT][FONT=Courier New][SIZE=2],cMsgboxOK,cMsgboxHand)[/SIZE][/FONT]
[FONT=Courier New][SIZE=2][COLOR=#0000ff]  FileClose[/COLOR][/SIZE][/FONT][FONT=Courier New][SIZE=2](c2)[/SIZE][/FONT]
[FONT=Courier New][SIZE=2][COLOR=#0000ff]End Sub[/COLOR][/SIZE][/FONT]
Again we handle unexpected errors with ErrorLabel.
First we check if the file exists. If it doesn't exist we exit this sub using Return.
A new reading connection is created using FileOpen and cRead flag.
We could have used 'c1' instead of 'c2' as the connection name (c2 is used to prevent confusion between the two parts).
The settings file is built of several known fields and an unknown number of fields as the ListBox items.
FileRead reads a single line from the connection.
Each time the next line will be read.
In the same order we previously saved the data, we now read the first three known lines.
The remaining data is now fetched and a new item is added to the ListBox for each line.
Each line is compared to the EOF constant which symbols the End Of File.
When the value equals to EOF we know that there are no more items left.
Another approach would have been to write the number of items before the items.

FileReadToEnd reads the entire remaining data (unlike FileRead which only reads the next single line).

Handling binary files could be done with FileGet and FilePut and with the BinaryFile library.
 

Attachments

mick

Member
Licensed User
Saving the second file???

Hello, Thats a very handy piece of code. Thanks so much for posting it. I was wondering however if anyone knows what to do with saving another file without
overwriting the first one. In other words, how do you check to see if a file exists and modify the file name if it does. Like test.txt exists so it saves as test2.txt. ect. Thanks, Mick
 

BjornF

Active Member
Licensed User
Dear Mick,

you can use the "FileExist(FileName)" (see the helpfile)

e.g. in your example

FileName="test"
suffix=".txt"

If FileExist(FileName & Suffix) = true Then FileName=FileName & "2"

... and then save the text as FileName & Suffix


all the best / Björn
 
Last edited:

mick

Member
Licensed User
Worked Like a Charm

GREAT! Thanks so much. That worked perfectly. I had a little trouble because
I left the brackets around (FileName & Suffix), once I removed them like
If FileExist FileName & Suffix = true Then FileName=FileName & "2"
Then it worked great. I really appreciate the info. Thanks again, Mick
 

mick

Member
Licensed User
One More Step

Hello again. You are right, I went back and looked and the brackets are there.
Now I need to wade out a little deeper.:)
Since I am using the FileWrite method to read the text in some controls and write it to a file, all the files are saved in the program folder of the app. ie
Program Files\My App.
How can I modify the file FileWrite to have the files saved in My Documents
so they are sync't automatically. I saw an example in here that used
Table1.SaveCSV("\My Documents\file.csv"...) To save a csv file but I can't see how I can use that, especially when I start the routine with "If FileExists" Can I get my app to look in my documents first and then write the file there. Thanks so much, Basic4PPC is the greatest development tool I've ever bought. I love it. Mick
 

mick

Member
Licensed User
Getting Closer

Thanks Erel. I'm almost there .I'm still having a bit of trouble because I'm first using
a variable to declare my file name.
MyFileName = "TestFile"
Suffix = ".txt"
If FileExist(MyFileName & Suffix) = true Then
MyFileName = "TestFile2"

That works Great,so when I try to inject the path like


MyFileName = "My Documents\TestFile"
Suffix = ".txt"
If FileExist(MyFileName & Suffix) = true Then
MyFileName = "My DocumentsTestFile2"

My error handler grabs it.
I Feel like such a pest here. I really do appreciate the amazing help I get here.
Thanks again Mick.
 

specci48

Well-Known Member
Licensed User
MyFileName = "My Documents\TestFile"
Suffix = ".txt"
If FileExist(MyFileName & Suffix) = true Then
MyFileName = "My Documents\TestFile2"
Is this missing \ a typo in you post or even in your code?

specci48
 

AppsByAaron

New Member
Licensed User
not working for me :(

I copied and pasted the code then tweaked it for my fields. I get an error when trying to run the program:

Error compiling program.
Error description: lstdata is not a known control or object.

Did I miss something? I can paste all my code (for this Sub) if you like.

Thanks.
 

specci48

Well-Known Member
Licensed User
Hi AppsByAaron

please look at the sample above (TextFiles.zip).
LstData is a listbox on form1.


specci48
 

KevG

New Member
Licensed User
Changing text file data

This has got me up to speed using text files, ta very to you all.

Using other programs I can change, let's say one line of a data file without overwriting the whole file.

I'm jiggered if I can fathom out how to do it in B4PPC.

Any thoughts, about this, most welcome.

Regards,

KevG
 

KevG

New Member
Licensed User
Thanks for that Erel,

Since posting the thread I've come to the same conclusion.
The Table / database / CSV file option seems to work and does just the job.

Ta,

KevG
 

treaki

New Member
Hi,

ugg, this bbcode problem makes the code nearly unreadable. I did some search/replace to make it readable. for everyone who can use it i paste it here whithout the bbcode problems again:

Saving code: (code Block 1)

B4X:
Sub SaveINI
  ErrorLabel(errSaveINI)
  FileOpen( c1,"TextFiles.ini",cWrite)
  FileWrite(c1,txtFirstName.Text)
  FileWrite(c1,txtLastName.Text)
  FileWrite(c1,chkValidAccount.Checked) 'writing a boolean value.
  For i = 0To lstData.Count-1
    FileWrite(c1,lstData.Item(i))
  Next
  FileClose(c1)
  Return'If there were no "Return" here, the error message would have shown.
  errSaveINI:
  Msgbox("Error writing INI file.","",cMsgboxOK,cMsgboxHand)
  FileClose(c1)
End Sub
Now for the reading... (code Block 2)

B4X:
Sub LoadINI
  ErrorLabel(errLoadINI)
  If Not(FileExist("TextFiles.ini")) Then Return
  FileOpen(c2,"TextFiles.ini",cRead)
  txtFirstName.Text = FileRead(c2)
  txtLastName.Text = FileRead(c2)
  chkValidAccount.Checked = FileRead(c2)
  s = FileRead(c2)
  DoUntil s = EOF 'Read the ListBox items.
  lstData.Add(s)
  s = FileRead(c2)
  Loop
  FileClose(c2)
  Return
  errLoadINI:
  Msgbox("Error reading INI file.","",cMsgboxOK,cMsgboxHand)
  FileClose(c2)
End Sub
greetings treaki
 

mjcoon

Well-Known Member
Licensed User
Hi,

ugg, this bbcode problem makes the code nearly unreadable. I did some search/replace to make it readable. for everyone who can use it i paste it here whithout the bbcode problems again:
...
greetings treaki
I don't think you will be able to resurrect this thread, nearly five years after the previous contribution...

Mike.
 
Top