This is a read-only snapshot of the ComputerCraft forums, taken in April 2020.
pazzesco's profile picture

Any Way To Read And Then Write A Raw Binary File All At Once?

Started by pazzesco, 21 July 2013 - 07:07 PM
pazzesco #1
Posted 21 July 2013 - 09:07 PM
Hey,

I've peeked around trying to find a way to read and then write a binary file all at once without corruption to no avail. My predicament is this:

Currently, opening a file using io.open(filename, "rb") will work (opens as binary read mode), but using filehandle.read("*a") doesn't seem to read the entire file. Instead, it seems to return just the first byte.

If opening the file with io.open(filename, "r"), the statement filehandle.read("*a") will return a string of the entire file's contents. The problem is that the content becomes corrupt when storing in a string.

Case in point:
fh=io.open("/1.gif","r")
r=fh:read("*a")
fh:close()

fh2=io.open("/2.gif","w")
fh2:write(r)
fh2:close()

The above code makes a corrupted copy of the gif file. When comparing the two files in a hex editor, it isn't in any specific spot (at the beginning or end). The file has corrupted values all throughout.

I considered reading the binary file byte by byte, but that stalls the program quickly and causes it to crash with a file of any real size. So to reiterate; is there any way to read and then write a raw binary file all at once?
theoriginalbit #2
Posted 22 July 2013 - 12:37 AM
No there are no read/write modes on file handles. With the way they work this would be impossible without the seek function, and seek is something we don't yet have.

The reason the values are becoming corrupted when storing in a string is one of 2 things;
1. You're not doing it correctly
2. You have managed to get the LuaJ bug that in a few cases effects characters >128.

But given that example I'm going to say that it's #2

Also in your "case in point" you didn't open them in binary mode… just sayin'

Using the below code I successfully made a copy of a 1.3 MB png file in 2.45 seconds.



local start = os.clock()

local iFile = fs.open("/test.png", "rb")
local oFile = fs.open("/test2.png", "wb")

local clock = os.clock() + 4

for s in iFile.read do
  oFile.write(s)
  if os.clock() >= clock then
	os.queueEvent("")
	coroutine.yield()
	clock = os.clock() + 4
  end
end

iFile.close()
oFile.close()

print(os.clock() - start)

It should be noted that the above code will never crash with a "failure to yield" as that is handled in the loop… also

os.queueEvent("")
coroutine.yield()
is the quickest form of yielding that can be performed in CC. I think someone figured out it was 0.01 seconds or something, meaning that it will not create the slowness in your program that `sleep(0)` or other such solutions do…
Edited on 21 July 2013 - 10:41 PM
Lyqyd #3
Posted 22 July 2013 - 01:05 AM
It may be advantageous to clear the stack while you're yielding in these by waiting for a specific event. It does delay a slight bit longer, I'd imagine, but you don't end up with the event queue in an unexpected state afterwards (or not quite so strange a state, anyway). Of course, that choice is up to the implementer, and it doesn't really matter hugely either way.
theoriginalbit #4
Posted 22 July 2013 - 01:55 AM
Yeh, it does slow it down by putting a filter on it, I assume it does something more on the server side… other option is just put a `sleep(0)` at the end of the script to prevent any queued events from being in the stack after the program has finished.