如何在Crystal中连续读取二进制文件并从中获取字节?

时间:2018-03-13 12:28:04

标签: binaryfiles crystal-lang

读取Crystal中的二进制文件应该使用// Input. const array1 = [['Tue Feb 20 09:00:00 GMT+01:00 2018', 'A1'], ['Tue Feb 21 09:00:00 GMT+01:00 2018', 'A1'],['Tue Feb 22 09:00:00 GMT+01:00 2018', 'A1']] const array2 = [['Tue Feb 20 09:00:00 GMT+01:00 2018', 'A2'], ['Tue Feb 21 09:00:00 GMT+01:00 2018', 'A2'],['Tue Feb 22 09:00:00 GMT+01:00 2018', 'A2'],['Tue Feb 23 09:00:00 GMT+01:00 2018', 'A2']] // Output. const output = [...new Map([...array1, ...array2].map(day => [day[0], day])).values()] // Log. console.log(output)Bytes.new(size)来完成,但是...如果你不知道你提前读了多少字节怎么办? ,你想一次继续读块吗?

这是一个例子,从一个假想的文件格式中读取3个块,该格式指定了具有初始字节的数据块的长度:

File#read

以下操作不起作用,因为file = File.open "something.bin", "rb" 无法连接(因为它实际上是Bytes,并且切片无法连接) :

Slice(UInt8)

我提出的最好的方法是使用data = Bytes.new(0) 3.times do bytes_to_read = file.read_byte.not_nil! chunk = Bytes.new(bytes_to_read) file.read(chunk) data += chunk end 代替Array(UInt8),并在读取的所有字节上调用Bytes

to_a

然而,似乎没有办法将其转回data = [] of UInt8 3.times do bytes_to_read = file.read_byte.not_nil! chunk = Bytes.new(bytes_to_read) file.read(chunk) data += chunk.to_a end Bytes was removed),这是许多应用程序和recommended by the authors to be the type of all binary data所需要的。

那么......我如何继续阅读文件连接到以前数据的结尾获取Array#to_slice

1 个答案:

答案 0 :(得分:3)

一种解决方案是在每次迭代时将数据复制到调整大小的字节。您还可以在容器(例如数组)中收集字节实例并在最后合并它们,但这都意味着额外的复制操作。

最好的解决方案可能是使用足够大的缓冲区来容纳可能被读取的所有数据 - 或者至少非常可能(如果需要,可以调整大小)。 如果最大大小只有3 * 255字节,这是一个明智的选择。如果缓冲区太大,您可以在最后调整大小。

data = Bytes.new 3 * UInt8::MAX
bytes_read = 0
3.times do
  bytes_to_read = file.read_byte.not_nil!
  file.read_fully(data + bytes_read)
  bytes_read += bytes_to_read
end
# resize to actual size at the end:
data = data[0, bytes_read]

注意:由于数据格式说明要读取的字节数,因此应使用read_fully而不是read,如果实际读取的字节数较少,则会默默忽略。

编辑:由于事先不知道块的数量和最大大小(每个注释),因此应使用动态调整大小的缓冲区。这可以使用IO::Memory轻松实现,如果需要,它将负责相应地调整缓冲区大小。

io = IO::Memory.new
loop do
  bytes_to_read = file.read_byte
  break if bytes_to_read.nil?
  IO.copy(file, io, bytes_to_read)
end
data = io.to_slice