在Ruby中修改缓冲区字节的快速可靠技术?

时间:2014-11-26 09:38:17

标签: ruby

我希望读取二进制文件的内容,对文件缓冲区中的每个字节执行二进制NOT,然后将修改后的缓冲区写回磁盘上的另一个文件。我目前使用的内容如下:

data = nil

::File.open( 'somefile.bin', 'rb' ) do | f |
    data = f.read( f.stat.size )
end

# unpack can sometimes throw an out of memory exception
raw_bytes = data.unpack( 'C*' )

raw_bytes.map! do | byte |
    ~byte
end

::File.open( 'somefile.bin.not', 'wb' ) do | f |
    f.write( raw_bytes.pack( 'C*' ) )
end

这样可行,但解压缩有时会抛出内存不足异常。是否可以直接编辑data缓冲区而无需将其解压缩到数组(我选择这样做,所以我可以使用map!来修改字节)。

由于需要对数以千计的文件执行(所有文件大小均为<= 30MB),因此性能非常重要。上述解决方案执行正常但由于内存不足问题而不可靠。我相信避免直接解包和修改数据缓冲区可能会避免这种情况。

任何人都可以改进我现有的解决方案吗?非常感谢。

2 个答案:

答案 0 :(得分:4)

  

我认为避免直接解包和修改数据缓冲区可能会避免这种情况。

您的数据缓冲区是二进制字符串,即0x00到0xFF范围内的字符序列。您可以通过将字符映射到反转范围0xFF到0x00来翻转字符的位:

0x00 (00000000) -> 0xFF (11111111)
0x01 (00000001) -> 0xFE (11111110)
0x02 (00000010) -> 0xFD (11111101)
0x03 (00000011) -> 0xFC (11111100)
...
0x7E (01111110) -> 0x81 (10000001)
0x7F (01111111) -> 0x80 (10000000)
0x80 (10000000) -> 0x7F (01111111)
0x81 (10000001) -> 0x7E (01111110)
...
0xFC (11111100) -> 0x03 (00000011)
0xFD (11111101) -> 0x02 (00000010)
0xFE (11111110) -> 0x01 (00000001)
0xFF (11111111) -> 0x00 (00000000)

应用字符到字符映射的最快方法可能是String#tr。您只需传递两个字符串abtra中的所有字符替换为b中的相应字符。

a = (0..255).map(&:chr).join #=> "\x00\x01\x02...\xFD\xFE\xFF"
b = a.reverse                #=> "\xFF\xFE\xFD...\x02\x01\x00"

由于"-""\\"tr中具有特殊含义,因此必须对其进行转义:

a.gsub!(/[\\-]/, '\\\\\0')
b.gsub!(/[\\-]/, '\\\\\0')

让我们看看它的表现如何:

require 'benchmark'

@data = IO.read('/dev/random', 30_000_000)

@a = (0..255).map(&:chr).join
@b = @a.reverse

@a.gsub!(/[\\-]/, '\\\\\0')
@b.gsub!(/[\\-]/, '\\\\\0')

Benchmark.bm(5) do |x|
  x.report("pack:") { @data.unpack('C*').map(&:~).pack('C*') }
  x.report("tr:")   { @data.tr(@a, @b) }
end

结果:

            user     system      total        real
pack:   4.780000   0.150000   4.930000 (  5.082274)
tr:     0.070000   0.000000   0.070000 (  0.078761)

答案 1 :(得分:1)

我每次尝试读取1mb,而不是将所有内容存储到内存中。在测试中我没有一个版本崩溃,所以我不能确定这个版本不会崩溃,但很有可能它不会失败。根据我的测试结果,作为奖励,我还设法获得了5%的适度性能提升(不要问我xD如何)。这是:

File.open( 'somefile.bin', 'rb' ) do | file |
    File.open( 'somefile.bin.not', 'wb' ) do | out |
        until file.eof?
            buffer = file.read( 1024*1024 ).unpack( 'C*' ).map do | byte |
                ~byte
            end

            out.write( buffer.pack( 'C*' ) )
        end
    end
end

如果您可以在您的环境中测试它并告诉我之后的结果会很好。