I want to write a list to a file and then read it back as a list. Then I want to append a new list to the end of the file and then read the file and get a list of 2 lists. i.e.
write {1,2}
then
append {3,4}
then read the file and get
{{1,2},{3,4}}
Here’s what I’ve got…
set target_file to ((path to desktop folder as text) & "theFile")
set list_1 to {1,2}
set open_target_file to open for access file target_file with write permission
write list_1 to open_target_file starting at eof as list
close access the open_target_file
set target_file to ((path to desktop folder as text) & "theFile")
set read_file to read file target_file as list
–result–> {1, 2}
set target_file to ((path to desktop folder as text) & "theFile")
set list_2 to {3,4}
set open_target_file to open for access file target_file with write permission
write list_2 to open_target_file starting at eof as list
close access the open_target_file
set target_file to ((path to desktop folder as text) & "theFile")
set read_file to read file target_file as list
I don’t either, of course, and yet within the file you can see that it wrote both: list long long list long long, yet it only recovers the first of them.
I strongly recommend reading Nigel Garvey’s article on the Ins and Outs of File Read/Write. He discusses how to extract (read back) types other than text.
I think you’ll have to get the length of the file and use “starting at” and “for” (both in bytes) to get your data back, but don’t recall details.
Alternatively, if you wait until tomorrow, he may answer himself.
Adam, I looked at that too… but then I wrote this to file {{1,2},{3,4}} in one shot
that file looked like list list long long list long long
So it’s got list list at the beginning of the file, so somehow when I append the data to the file it doesn’t come out the same. Right now I guess I’ll have to first read the file into a variable and add my new list to the end of the variable and then overwrite the whole file with the variable. I checked it and it works.
Like this it works, if I first set this_list to {1,2} and run the code, then set this_list to {3,4} and run it… it reads perfect. The “if file exists” stuff is to make sure I don’t get an error if there is no file yet.
set this_list to {1, 2}
set target_file to ((path to desktop folder as text) & "theList")
set the_list to {}
tell application "Finder"
if file target_file exists then
set read_List to read file target_file as list
set beginning of the_list to read_List
set end of the_list to this_list
else
set the_list to this_list
end if
end tell
try
set open_target_file to open for access file target_file with write permission
set eof of the open_target_file to 0
write the_list to open_target_file starting at eof as list
close access the open_target_file
on error
try
close access the open_target_file
end try
end try
It seems crazy to have to do it this way… I hope someone knows how to just append something and have it work. I read Nigel Garvey’s article on the Ins and Outs of File Read/Write, unfortunately he never mentions how to append a list to a list in a file.
I just found this in Nigel’s article but I don’t know how to implement it…
I tried this and it didn’t work for my original example:
set target_file to ((path to desktop folder as text) & "theList")
set read_file1 to read file target_file as list
set read_file2 to read file target_file as list
set read_file to {read_file1, read_file2}
– result → {{1, 2}, {1, 2}}
it never saw the {3,4}, so I’m not sure what he means about “leaving the file mark advanced as appropriate”
The number of items in the list is written in the list header in the 5th through 8th bytes. You could change the number, but it’s much easier to do what you did (i.e. read list, make new list, write new list). Here’s an example of just changing the number of items from 1 to 2.
set c to ASCII character 2 -- want to write number 2 to eight byte
set l1 to {1, 2}
set l2 to {3, 4}
set file_spec to ((path to desktop as string) & "list2.txt") as file specification
set ref_num to open for access file_spec with write permission
write {l1} to ref_num as list
write l2 to ref_num as list
write c to ref_num starting at 8 -- change number of items to 2
close access ref_num
set t to read file_spec as list
Here I just wrote 2 to the eighth byte, so the script would know there are two items in the list.
That’s because you can’t (without specialist knowledge) append anything to a list in a file. You can append another list to the end of the file, but not to the end of the list in it. You can, however, read the existing list into another list, put the new list into that too, and write the whole thing back to the file “ as you’ve already done.
To do it this way, you need to open the file for access first so that the file mark is preserved and the second read continues from where the first finishes. The way you’ve done it, the file’s closed after each read and the file mark’s lost. When the file’s reopened for the second read, it starts at the beginning again. The code below leaves the file open and keeps reading in lists from it until the eof’s reached. Then it closes the file.
set target_file to ((path to desktop folder as text) & "theList")
try
target_file as alias
on error
display dialog "File "" & target_file & "" doesn't exist!" buttons {"Cancel"} default button 1 with icon stop
error number -128
end try
-- This assumes that the file contains nothing but lists.
set the_list to {}
set open_target_file to (open for access file target_file)
repeat
try
set end of the_list to (read open_target_file as list)
on error
-- Hopefully an "end of file" error.
exit repeat
end try
end repeat
close access open_target_file
the_list
Alright Nigel! That’s the solution. Thanks so much for putting off your bed time to help because I could not have gone to bed before I figured this out. You saved me much grief.
I knew I was missing something simple… I had to leave the file open before the second read. Awesome!
Actually, it is! Assuming that the file contains a list of lists or records, you just need to read the current value of kel’s bytes and increment it. Once you’ve added error traps, it’s as much bother as any other method, but it’s doable and could be handy to know.
set target_file to (path to desktop as Unicode text) & "Test file"
append_to_list_in_file(target_file, {a:"Hello"})
read file target_file as list
on append_to_list_in_file(target_file, the_item)
-- This handler needs more development to work with anything other than lists or records.
if (the_item's class is not in {list, record}) then error "append_to_list_in_file(): the_item is not a list or a record."
set target_file to target_file as Unicode text
try
set fRef to (open for access file target_file with write permission)
set file_length to (get eof fRef)
if (file_length is 0) then
-- If the file's empty, write a list containing the item to it.
write {the_item} to fRef
else if (file_length < 17) or ((read fRef from 1 to 4) is not "LIST") or ((read fRef from 9 to 12) is not in {"LIST", "reco"}) then
-- If the file doesn't contain a list of lists or records, throw an error. (The test isn't entirely foolproof.)
error "append_to_list_in_file(): The file doesn't contain a list of lists or records."
else
-- Othewise append the item to the exisiting list in the file, incrementing the list's length data.
set new_list_length to (read fRef from 5 to 8 as integer) + 1
write new_list_length to fRef starting at 5
write the_item to fRef starting at eof
end if
on error msg
display dialog msg buttons {"OK"} default button 1
end try
close access fRef
end append_to_list_in_file
Not that I can see. It certainly makes for shorter code.
set target_file to (path to desktop as Unicode text) & "Test file.scpt"
try
set o to (load script file target_file)
on error
script
property the_list : {}
end script
set o to result
end try
set end of o's the_list to {1, 2}
store script o in file target_file replacing yes
the_list of (load script file target_file)
The file size to contain {{1, 2}, {3, 4}} using this method is 2774 bytes, whereas using File Read/Write it’s 72 bytes. There’s no subjective time difference with any of the scripts. In timed tests, with each method saving 50 copies of the same two-integer list from scratch to the method’s own file, the results on my G5 are:
regulus’s first script (simply appending flat lists to the file): 0.094 sec
regulus’s read-append-rewrite script (optimised by me, not shown): 0.351 sec
kel/NG (appending lists to directly to the list in the file): 0.086 sec
Script object: 0.471 sec
Swapping the running order makes regulus’s first script slightly faster than kel/NG. There’s very little between these two.
Thanks for posting the follow through. It’s a good thing I didn’t post my incrementer without all the new things they added after Jaguar to the read/write command.