从 Ruby 中的二进制文件中提取十六进制字符串
Extract hex strings from binary file in Ruby
对于二进制文件,我想提取绿色(日期和小时)和蓝色的十六进制字符串。蓝色的十六进制字符串介于字节 09 和 00 之间。
我已经能够使用正则表达式提取日期和时间以及部分蓝色的十六进制字符串。为此,我将字节 09 (\x09)
设置为 "line separator"
我遇到的问题可能可以通过正则表达式得到 09 和 00 之间的字符串来解决,但目前我的正则表达式 (^20.*) 越来越不受欢迎
和非 ascii 字节。有人可以帮我只获取 09 和 00 之间的字节吗?
我当前的代码:
BEGIN{ $/="\x09".force_encoding("BINARY") }
IO.foreach("file.dat"){ |l|
line = l.unpack('H*')[0]
next unless line =~ /(.{8}2d.{4}2d.{4})20(.{4}3a.{4}3a.{4})|(^20.*)/
if ( != nil and != nil )
date =
hour =
p date.gsub(/../) { |b| b.hex.chr }
p hour.gsub(/../) { |b| b.hex.chr }
end
if != nil
p .gsub(/20/,"").gsub(/../) { |b| b.hex.chr }
end
}
当前输出
"2017-10-19"
"15:43:27"
"83492624790981030E100000\x00\x18\v\x16\x84\x13\x05$B#q\x000\x03\x81\x01\n\x00\x00v\x00\x0000000003\t"
"2017-12-05"
"09:32:15"
"001104059419632801001B237100300381010A0000\x00\x00\x00\x00\x02\xD0\x00\x00\x00\b\xFEF\xCC\x00\x06\xE7\f\x13\x0F+\e\xB5\xE1/\x00\xB5\x83I&$y\t"
=> nil
预期输出
"2017-10-19"
"15:43:27"
"83492624790981030E100000"
"2017-12-05"
"09:32:15"
"001104059419632801001B237100300381010A0000"
=> nil
文件如下所示:
附加示例文件:
file.dat
为了获得以 20
开头并以 00
结尾的字节,您需要像这样更改正则表达式:
next unless line =~ /(.{8}2d.{4}2d.{4})20(.{4}3a.{4}3a.{4})|^20(.*?0?)0{2}/
基本上我只将正则表达式的最后一部分从 (^20.*)
更改为 ^20(.*?0?)0{2}
。
解释如下:
- 从 20 开始 -
^20
- 尽可能少匹配 -
.*?
- 直到你得到两个连续的 0
0{2}
.*?
之后的 0?
处理您有 X0 00
的情况
此外,我没有将 20
包括在捕获的组中,因为无论如何您稍后都会在代码中删除它,因此您可以删除 .gsub(/20/, '')
中的
p .gsub(/20/,"").gsub(/../) { |b| b.hex.chr }
require 'date'
IN_FNAME = "file.dat"
OUT_FNAME = "out_file.dat"
END_OF_LINE = "\x09"
str_out = ''
File.foreach(IN_FNAME, sep=END_OF_LINE) do |line|
dt_str = line[3..21]
if (DateTime.strptime(dt_str, '%Y-%m-%d %H:%M:%S') rescue nil)
puts dt_str.split(' ')
next
end
arr = line.unpack("C*")
next unless arr.first == 32
a = arr.map(&:chr).select { |c| c.match? /\d/ }
puts a.join
str_out << a.map(&:ord).pack("C*")
end
2017-10-19
15:43:27
83492624790981030100000000000003
2017-12-05
09:32:15
0011040594196328010012371003003810100000
最后一步是写入二进制文件OUT_FNAME
。
File.binwrite(OUT_FNAME, str_out)
#=> 72
可以看到,写入了72个字节。这是使用十六进制编辑器查看时该文件的屏幕截图。
这可以与问题中显示的屏幕截图进行比较。
我们可以读取该文件以确认其写入正确。
File.binread(OUT_FNAME)
#=> "834926247909810301000000000000030011040594196328010012371003003810100000"
见DateTime::strptime and String#unpack。
请注意,要打印日期和时间都必须有效。例如,"0000-19-39 29:00:00"
将被简单的正则表达式接受,将被跳过。
对于二进制文件,我想提取绿色(日期和小时)和蓝色的十六进制字符串。蓝色的十六进制字符串介于字节 09 和 00 之间。
我已经能够使用正则表达式提取日期和时间以及部分蓝色的十六进制字符串。为此,我将字节 09 (\x09)
设置为 "line separator"我遇到的问题可能可以通过正则表达式得到 09 和 00 之间的字符串来解决,但目前我的正则表达式 (^20.*) 越来越不受欢迎 和非 ascii 字节。有人可以帮我只获取 09 和 00 之间的字节吗?
我当前的代码:
BEGIN{ $/="\x09".force_encoding("BINARY") }
IO.foreach("file.dat"){ |l|
line = l.unpack('H*')[0]
next unless line =~ /(.{8}2d.{4}2d.{4})20(.{4}3a.{4}3a.{4})|(^20.*)/
if ( != nil and != nil )
date =
hour =
p date.gsub(/../) { |b| b.hex.chr }
p hour.gsub(/../) { |b| b.hex.chr }
end
if != nil
p .gsub(/20/,"").gsub(/../) { |b| b.hex.chr }
end
}
当前输出
"2017-10-19"
"15:43:27"
"83492624790981030E100000\x00\x18\v\x16\x84\x13\x05$B#q\x000\x03\x81\x01\n\x00\x00v\x00\x0000000003\t"
"2017-12-05"
"09:32:15"
"001104059419632801001B237100300381010A0000\x00\x00\x00\x00\x02\xD0\x00\x00\x00\b\xFEF\xCC\x00\x06\xE7\f\x13\x0F+\e\xB5\xE1/\x00\xB5\x83I&$y\t"
=> nil
预期输出
"2017-10-19"
"15:43:27"
"83492624790981030E100000"
"2017-12-05"
"09:32:15"
"001104059419632801001B237100300381010A0000"
=> nil
文件如下所示:
附加示例文件: file.dat
为了获得以 20
开头并以 00
结尾的字节,您需要像这样更改正则表达式:
next unless line =~ /(.{8}2d.{4}2d.{4})20(.{4}3a.{4}3a.{4})|^20(.*?0?)0{2}/
基本上我只将正则表达式的最后一部分从 (^20.*)
更改为 ^20(.*?0?)0{2}
。
解释如下:
- 从 20 开始 -
^20
- 尽可能少匹配 -
.*?
- 直到你得到两个连续的 0
0{2}
.*?
之后的0?
处理您有X0 00
的情况
此外,我没有将 20
包括在捕获的组中,因为无论如何您稍后都会在代码中删除它,因此您可以删除 .gsub(/20/, '')
中的
p .gsub(/20/,"").gsub(/../) { |b| b.hex.chr }
require 'date'
IN_FNAME = "file.dat"
OUT_FNAME = "out_file.dat"
END_OF_LINE = "\x09"
str_out = ''
File.foreach(IN_FNAME, sep=END_OF_LINE) do |line|
dt_str = line[3..21]
if (DateTime.strptime(dt_str, '%Y-%m-%d %H:%M:%S') rescue nil)
puts dt_str.split(' ')
next
end
arr = line.unpack("C*")
next unless arr.first == 32
a = arr.map(&:chr).select { |c| c.match? /\d/ }
puts a.join
str_out << a.map(&:ord).pack("C*")
end
2017-10-19
15:43:27
83492624790981030100000000000003
2017-12-05
09:32:15
0011040594196328010012371003003810100000
最后一步是写入二进制文件OUT_FNAME
。
File.binwrite(OUT_FNAME, str_out)
#=> 72
可以看到,写入了72个字节。这是使用十六进制编辑器查看时该文件的屏幕截图。
这可以与问题中显示的屏幕截图进行比较。
我们可以读取该文件以确认其写入正确。
File.binread(OUT_FNAME)
#=> "834926247909810301000000000000030011040594196328010012371003003810100000"
见DateTime::strptime and String#unpack。
请注意,要打印日期和时间都必须有效。例如,"0000-19-39 29:00:00"
将被简单的正则表达式接受,将被跳过。