Writing binary data to file using write_bytes inserts extra zeros?

Example on Linux Mint, Crystal 1.6.2 [879691b2e] (2022-11-03)

data = UInt16[
0xD0, 0xCF, 0x11, 0xE0, 0xA1, 0xB1, 0x1A, 0xE1, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
....... deleted ..............
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
]

filename = "test.ole"
file = File.new(filename,"wb")
data.each do |x|
  file.write_bytes(x)
end
file.close

Inspecting this file with ghex shows extra zeros which is NOT what I want
(first couple of bytes are shown)

D0 00 CF 00 11 00 E0 00 ........

I think you meant to use #write_byte. The method you’re using is writing the binary representation of a UInt16, which takes two bytes to represent each value. However since they’re all less than 255, the second byte technically isn’t needed and is correctly zero.

If these values are indeed intended to be used to represent bytes, consider using Bytes - Crystal 1.8.2. This would allow you to simplify your logic to essentially just:

bytes = Bytes[0xD0, 0xCF, 0x11, 0xE0, 0xA1, 0xB1, 0x1A, 0xE1]

File.open("test.old", "wb") do |file|
  file.write bytes
end

The thing is that write_bytes insert a zero irrespective of the value I want to write. These values are and should be UInt16, so a value of 0xD0 in the input becomes “D0 00” in the output file.

Long story short: your solution works.
Thanks

Right, isn’t that expected? UInt16 needs two bytes to fully represent all the values, in your case tho you’re treating a single byte as a UInt16 which is only ever going to use the first byte, leaving the second as always 00.

Ultimately comes down to are the values actually UInt16 or are they bytes?

The UInt16 needs two bytes, perhaps I am missing something but 0xD0 is 2 bytes, 16 bits in total so I thought I need to use UInt16?

Isn’t 0xD0 one byte? Each [0-9a-f] character being 4 bits (a nibble), and two of those being 8 bits (a byte). So 0xD0 0x00 would be two bytes.

You are right, I feel really silly now. Sorry for wasting your time, I should have known better.

Off by one error :) glad we figured it out!

1 Like

You are right, I feel really silly now. Sorry for wasting your time, I should have known better.