Simple file I/O issue
Simple file I/O issue
Hey all, I'm a new convert to freebasic from QB 4.5!
I am having just one issue though: files that I create in freebasic, cannot seem to be read by freebasic.
For example:
LET A = 1
LET B$ = "HELLO"
LET C$ = "TESTING"
OPEN "FILE.DAT" FOR RANDOM AS #1
PUT #1, 1, A
PUT #1, 2, B#
PUT #1, 3, C$
CLOSE #1
Should create a three-line file which looks like this:
1
HELLO
TESTING
However, it does not. When I run a program to input the data from the saved file:
OPEN "FILE.DAT" FOR RANDOM AS #1
GET #1, 1, A
GET #1, 2, B$
GET #1, 3, C$
PRINT A
PRINT B
PRINT C
CLOSE #1
That program should print, on my screen:
1
HELLO
TESTING
But it does not.
It manages to input the integer variable (A) from of line #1, but it cannot pull the string variables (B$ and C$) out of lines 2 and 3.
So my screen looks like this:
1
And that's it.
If it makes a difference, I'm running freebasic on Ubuntu Linux.
Thanks!
I am having just one issue though: files that I create in freebasic, cannot seem to be read by freebasic.
For example:
LET A = 1
LET B$ = "HELLO"
LET C$ = "TESTING"
OPEN "FILE.DAT" FOR RANDOM AS #1
PUT #1, 1, A
PUT #1, 2, B#
PUT #1, 3, C$
CLOSE #1
Should create a three-line file which looks like this:
1
HELLO
TESTING
However, it does not. When I run a program to input the data from the saved file:
OPEN "FILE.DAT" FOR RANDOM AS #1
GET #1, 1, A
GET #1, 2, B$
GET #1, 3, C$
PRINT A
PRINT B
PRINT C
CLOSE #1
That program should print, on my screen:
1
HELLO
TESTING
But it does not.
It manages to input the integer variable (A) from of line #1, but it cannot pull the string variables (B$ and C$) out of lines 2 and 3.
So my screen looks like this:
1
And that's it.
If it makes a difference, I'm running freebasic on Ubuntu Linux.
Thanks!
-
- Site Admin
- Posts: 6323
- Joined: Jul 05, 2005 17:32
- Location: Manchester, Lancs
Re: Simple file I/O issue
Hi skystrick,
It looks to me like FB might have a problem with RANDOM file I/O.
If I compile with error checking (fbc -exx), then I get a file I/O error on 'PUT #1, 1, A'.
I think it's because for some reason we require PUTs in RANDOM mode to be the same length as the record length, which is 128 bytes by default.
If it will help, we can suggest some workarounds. Do you need to keep your data saved in the same file format as before?
It looks to me like FB might have a problem with RANDOM file I/O.
If I compile with error checking (fbc -exx), then I get a file I/O error on 'PUT #1, 1, A'.
I think it's because for some reason we require PUTs in RANDOM mode to be the same length as the record length, which is 128 bytes by default.
If it will help, we can suggest some workarounds. Do you need to keep your data saved in the same file format as before?
Re: Simple file I/O issue
Okay, here's what I need to be able to do:counting_pine wrote:Hi skystrick,
It looks to me like FB might have a problem with RANDOM file I/O.
If I compile with error checking (fbc -exx), then I get a file I/O error on 'PUT #1, 1, A'.
I think it's because for some reason we require PUTs in RANDOM mode to be the same length as the record length, which is 128 bytes by default.
If it will help, we can suggest some workarounds. Do you need to keep your data saved in the same file format as before?
I need to be able to output the content of variables to files, and then retrieve that content from files. Any given file will contain variables of various types (integers, strings, etc.) and varying lengths. Also, I make use of user-defined types which contain sub-records of varying lengths and variable types; for example:
Code: Select all
TYPE TRANSACTION
TRANSEQ AS INTEGER
TRANDATE AS STRING * 8
PAYEE AS STRING * 20
CHECK AS STRING * 4
TRANTYPE AS STRING * 1
AMOUNT AS DOUBLE
RUNBAL AS DOUBLE
END TYPE
DIM EXAMPLE AS TRANSACTION
I just need to know how to accomplish the same ends in freebasic. I am used to using RANDOM mode, because BINARY requires that I know the exact length of every record before the one I want, so that I know which byte to point to in the file, and becomes a pain in the butt when dealing with multi-part records such as the user-defined type shown above. It was always just so simple, quick, and easy to just be able to say "retrieve record X from file Y and drop into into the user-defined type TRANSACTION as variable EXAMPLE" with the TYPE, DIM, and GET/PUT statements.
I'm porting a program I wrote under QB, and I've gotten everything to work except this file I/O issue, and the LPRINT command.
Thanks again!
Re: Simple file I/O issue
This emulates the Lprint command by writing to a file.
You can then later Open, Edit and Print that file under OS control.
Make sure that you use Freefile for all files, or hard-code printer as a fixed file number.
You can then later Open, Edit and Print that file under OS control.
Make sure that you use Freefile for all files, or hard-code printer as a fixed file number.
Code: Select all
'===================================================================
' Lprint and Lprint Using to a file
'===================================================================
' At the start of your program
Dim As String printfile = "lineprint.txt"
Dim As Integer printer = Freefile
Open printfile For Output As #printer
#UnDef Lprint
#Define Lprint Print #printer,
'-------------------------------------------------------------------
' within your program
Lprint "Hello world."
Lprint "Goodbye world."
'-------------------------------------------------------------------
' At the end of the program close the file
Close #printer
'===================================================================
Print "Normal exit."
Sleep
'===================================================================
-
- Site Admin
- Posts: 6323
- Joined: Jul 05, 2005 17:32
- Location: Manchester, Lancs
Re: Simple file I/O issue
I think the most foolproof way to do what you want is to create routines to get/put your data.
The Put routine will pack all the data into a string of a known size, then Put that string.
The Get routine Gets into a string of a known size, and then unpacks all the fields and stores them in the type.
This is necessary to bypass a handful of size/alignment issues that FB has when creating types as compared to QB, particularly with fixed-length strings.You may notice the use of MKI$/CVI and MKD$/CVD to pack/unpack INTEGER and DOUBLE variables. Functions like this convert numerical data to/from a string representation containing the bytes of the data as they would be found in memory.
Edit: updated to avoid Getting/Putting a var-len String, since it will have a two-byte header prepended in QB.
The Put routine will pack all the data into a string of a known size, then Put that string.
The Get routine Gets into a string of a known size, and then unpacks all the fields and stores them in the type.
This is necessary to bypass a handful of size/alignment issues that FB has when creating types as compared to QB, particularly with fixed-length strings.
Code: Select all
TYPE TRANSACTION '' element size, offset
TRANSEQ AS INTEGER '' 1, 2
TRANDATE AS STRING' * 8 '' 3, 8
PAYEE AS STRING' * 20 '' 11,20
CHECK AS STRING' * 4 '' 31, 4
TRANTYPE AS STRING' * 1 '' 35, 1
AMOUNT AS DOUBLE '' 36, 8
RUNBAL AS DOUBLE '' 44, 8
END TYPE '' total size: 52-1 = 51
DIM EXAMPLE AS TRANSACTION
SUB PUTTRANSACTION(FILENUM AS INTEGER, START AS LONG, T AS TRANSACTION)
DIM REC AS STRING*51
REC = SPACE$(51)
MID$(REC, 1, 2) = MKI$(T.TRANSEQ)
MID$(REC, 3, 8) = T.TRANDATE
MID$(REC, 11, 20) = T.PAYEE
MID$(REC, 31, 4) = T.CHECK
MID$(REC, 35, 1) = T.TRANTYPE
MID$(REC, 36, 8) = MKD$(T.AMOUNT)
MID$(REC, 44, 8) = MKD$(T.RUNBAL)
PUT #FILENUM, START, REC
END SUB
SUB GETTRANSACTION(FILENUM AS INTEGER, START AS LONG, T AS TRANSACTION)
DIM REC AS STRING*51
REC = SPACE$(51)
GET #FILENUM, START, REC
T.TRANSEQ = CVI(MID$(REC, 1, 2))
T.TRANDATE = MID$(REC, 3, 8)
T.PAYEE = MID$(REC, 11, 20)
T.CHECK = MID$(REC, 31, 4)
T.TRANTYPE = MID$(REC, 35, 1)
T.AMOUNT = CVD( MID$(REC, 36, 8))
T.RUNBAL = CVD( MID$(REC, 44, 8))
END SUB
OPEN "FILE.DAT" FOR RANDOM AS #1 LEN=51
GETTRANSACTION 1, 1, EXAMPLE
CLOSE #1
OPEN "FILE.DAT" FOR RANDOM AS #1 LEN=51
PUTTRANSACTION 1, 1, EXAMPLE
CLOSE #1
Edit: updated to avoid Getting/Putting a var-len String, since it will have a two-byte header prepended in QB.
Re: Simple file I/O issue
Welcome , skystrick. To be familiar with random files take alook on this:
http://www.freebasic.net/forum/viewtopi ... =3&t=20150
http://www.freebasic.net/forum/viewtopi ... =2&t=19931
http://www.petesqbsite.com/forum/viewtopic.php?t=3354 and others examples for random files on this site.
and use hexa printout of your file contents (via notepad) to catch where is difference in field element's length.
Pete
http://www.freebasic.net/forum/viewtopi ... =3&t=20150
http://www.freebasic.net/forum/viewtopi ... =2&t=19931
http://www.petesqbsite.com/forum/viewtopic.php?t=3354 and others examples for random files on this site.
and use hexa printout of your file contents (via notepad) to catch where is difference in field element's length.
Pete
Re: Simple file I/O issue
simplest way:
As you can see with this, if you omit the second parameter of put - "put #f,, example" - then the function will store the whole data structure of the user defined type to the file, and get will do the same to automatically sort the data into the correct slots. This will not work with pointers though - if the UDT has a pointer as a member, it will only store the memory address of the pointer and not the data stored at that address.
Code: Select all
TYPE TRANSACTION
TRANSEQ AS INTEGER
TRANDATE AS STRING * 8
PAYEE AS STRING * 20
CHECK AS STRING * 4
TRANTYPE AS STRING * 1
AMOUNT AS DOUBLE
RUNBAL AS DOUBLE
END TYPE
DIM AS TRANSACTION EXAMPLE, example2
with example
.transeq = 1
.trandate = "May 20th"
.payee = "John Doe"
.check = "3184"
.trantype = "S"
.amount = 24.98
.runbal = 120.34
end with
dim as integer f
f = freefile
open "test.dat" for random as #f
put #f,, example
close #f
open "test.dat" for random as #f
get #f,, example2
close #f
with example2
print .transeq
print .trandate
print .payee
print .check
print .trantype
print .amount
print .runbal
end with
sleep
Re: Simple file I/O issue
But if you compile with error checking (fbc -exx), this no more works and you get a file I/O error on 'put #f,, example'.Merick wrote:simplest way:
As said counting_pine, you must provide 128 bytes per default.
One solution is to use this UDT:
Code: Select all
TYPE TRANSACTION FIELD = 1
TRANSEQ AS INTEGER
TRANDATE AS STRING * 8
PAYEE AS STRING * 20
CHECK AS STRING * 4
TRANTYPE AS STRING * 1
AMOUNT AS DOUBLE
RUNBAL AS DOUBLE
dummy(1 to 71) as byte
END TYPE
Code: Select all
TYPE TRANSACTION FIELD = 1
TRANSEQ AS INTEGER
TRANDATE AS STRING * 8
PAYEE AS STRING * 20
CHECK AS STRING * 4
TRANTYPE AS STRING * 1
AMOUNT AS DOUBLE
RUNBAL AS DOUBLE
END TYPE
Type TRANSACTIONwithPADDING Extends TRANSACTION FIELD = 1
dummy(1 to 128-sizeof(TRANSACTION)) as byte
END Type
DIM AS TRANSACTIONwithPADDING EXAMPLE, example2
with example
.transeq = 1
.trandate = "May 20th"
.payee = "John Doe"
.check = "3184"
.trantype = "S"
.amount = 24.98
.runbal = 120.34
end with
dim as integer f
f = freefile
open "test.dat" for random as #f
put #f,, example
close #f
open "test.dat" for random as #f
get #f,, example2
close #f
with example2
print .transeq
print .trandate
print .payee
print .check
print .trantype
print .amount
print .runbal
end with
sleep
Re: Simple file I/O issue
From PowerBasic I am drilled to everytime using of 'LEN' for RANDOM file access! You MUST to know length of record.
So working code is ( compiler linux option fbc -exx -w pedantic "%f" )
Pete
So working code is ( compiler linux option fbc -exx -w pedantic "%f" )
Code: Select all
screen 18
TYPE TRANSACTION
TRANSEQ AS INTEGER
TRANDATE AS STRING * 8
PAYEE AS STRING * 20
CHECK AS STRING * 4
TRANTYPE AS STRING * 1
AMOUNT AS DOUBLE
RUNBAL AS DOUBLE
END TYPE
DIM AS TRANSACTION example, example2
with example
.transeq = 1
.trandate = "May 20th"
.payee = "John Doe"
.check = "3184"
.trantype = "S"
.amount = 24.98
.runbal = 120.34
end with
dim as integer f
f = freefile
'open "test.dat" for random as #f
open "test.dat" for random as #f LEN=Len(example)
put #f,, example
close #f
'open "test.dat" for random as #f
open "test.dat" for random as #f LEN=Len(example2)
get #f,, example2
close #f
with example2
print .transeq
print .trandate
print .payee
print .check
print .trantype
print .amount
print .runbal
end with
sleep
end
Last edited by petan on May 20, 2013 19:43, edited 1 time in total.
Re: Simple file I/O issue
Okay, this seems the simplest of everything here. Make every record be 128 bytes by padding it out with blank space. Three questions:fxm wrote: One solution is to use this UDT:Code: Select all
TYPE TRANSACTION FIELD = 1 TRANSEQ AS INTEGER TRANDATE AS STRING * 8 PAYEE AS STRING * 20 CHECK AS STRING * 4 TRANTYPE AS STRING * 1 AMOUNT AS DOUBLE RUNBAL AS DOUBLE dummy(1 to 71) as byte END TYPE
(1) Is there not a way, perhaps using a LEN argument, to redefine the length of the the records in a file from the default of 128? If I know that I have a file which exclusively contains these TYPE TRANSACTION records, each of which is 57 characters long, can't I use a LEN arguement with my GET and PUT statements so that I don't have this issue, and don't artifically increase the size of data stored on limited-size disks (floppies) by padding it out with useless filler data?
(2) Is there a plan to fix this in future editions of FreeBASIC, so that it's more in-line with the simplicity of GET and PUT statements under QB?
(3) Okay, so I see how to solve the issue with files where each record is of TYPE TRANSACTION, because they're of predictable length (same every time). But let's say I'm working with a file which doesn't use the TYPE TRANSACTION, and contains variables of different types and lengths. As in my original example:
LET A = 1
LET B$ = "HELLO"
LET C$ = "TESTING"
OPEN "FILE.DAT" FOR RANDOM AS #1
PUT #1, 1, A
PUT #1, 2, B$
PUT #1, 3, C$
CLOSE #1
Should create a three-line file which looks like this:
1
HELLO
TESTING
However, it does not. When I run a program to input the data from the saved file:
OPEN "FILE.DAT" FOR RANDOM AS #1
GET #1, 1, A
GET #1, 2, B$
GET #1, 3, C$
PRINT A
PRINT B
PRINT C
CLOSE #1
That program should print, on my screen:
1
HELLO
TESTING
But it does not.
It manages to input the integer variable (A) from of line #1, but it cannot pull the string variables (B$ and C$) out of lines 2 and 3.
So my screen looks like this:
1
And that's it.
Re: Simple file I/O issue
See the previous post of petan.
Read at documentation KeyPgType
and specially the paragraph 'Dynamic data'
It is why there is more simple to work with each fix-len string length in UDT that overvalues all the useful string sizes, and also a fix record lenght for file that also overvalues all the useful UDT sizes.
So, we can always read/write the Nth record with 'Get/Put #file_number, N, UDT_instance'.
Read at documentation KeyPgType
and specially the paragraph 'Dynamic data'
It is why there is more simple to work with each fix-len string length in UDT that overvalues all the useful string sizes, and also a fix record lenght for file that also overvalues all the useful UDT sizes.
So, we can always read/write the Nth record with 'Get/Put #file_number, N, UDT_instance'.
-
- Site Admin
- Posts: 6323
- Joined: Jul 05, 2005 17:32
- Location: Manchester, Lancs
Re: Simple file I/O issue
I've posted a bug report about FB's Random incompatibilities at #673. I don't see why they shouldn't be fixable.
Unfortunately, an additional incompatibility with QB lies with using String*n in a structure, because it uses a different size internally. This means the naive method of reading/writing will not produce files that are compatible with QB. It's why for the moment I advise packing the data into a fixed-length String.
Unfortunately, an additional incompatibility with QB lies with using String*n in a structure, because it uses a different size internally. This means the naive method of reading/writing will not produce files that are compatible with QB. It's why for the moment I advise packing the data into a fixed-length String.