Ruby - 将"=="十六进制值与字符串进行比较

4

我基本上是在读取图片文件的头部并进行快速比较,以确定它实际上是什么类型的文件。BMP、GIF、PNG都很容易,因为它们的头部分别包含BM、GIF和PNG来标识自己。但JPG让我有些困惑。

JPG的前3个字节通常为0xff\0xd8\0xff,但我怎么设置都无法在简单的比较中得到真值。

我读取了前4个字节:

if data[0, 3] == "\xff\xd8\xff"
    puts "This is a JPG"
end

我知道我离成功很近,但就是无法让它正常工作。请告诉我这里缺少了什么。

注意:我知道有一些gem可以帮我完成这个任务,但我不想使用gem,就这么简单。


你的数据长什么样? - Arup Rakshit
@Anand 我使用 data[0, 3] 来获取我读取的前4个字节中的前3个字节。 - user1360461
4个回答

9
这是一个字符编码问题。从JPEG中读取前4个字节会返回一个ASCII编码的字符串:
head = File.read("some.jpg", 4)
# => "\xFF\xD8\xFF\xE1"

head.encodig
# => #<Encoding:ASCII-8BIT>

另一方面,字符串是采用UTF-8编码的:

jpg_prefix = "\xff\xd8\xff"
# => "\xFF\xD8\xFF"

jpg_prefix.encoding
# => #<Encoding:UTF-8>

比较UTF-8和ASCII字符串的结果与预期不符:

head[0,3] == jpg_prefix
# => false

您需要使用String#force_encoding显式设置编码:

jpg_prefix = "\xff\xd8\xff".force_encoding(Encoding::ASCII_8BIT)
# => "\xFF\xD8\xFF"

jpg_prefix.encoding
# => #<Encoding:ASCII-8BIT>

head[0,3] == jpg_prefix
# => true

使用Integer#chr创建ASCII字符,然后连接它们(正如Mario Visic建议的那样)也可以实现此功能:
jpg_prefix = 0xff.chr + 0xd8.chr + 0xff.chr
# => "\xFF\xD8\xFF"

jpg_prefix.encoding
# => #<Encoding:ASCII-8BIT>

或者使用Array#pack

jpg_prefix = ["FFD8FF"].pack("H*")
# => "\xFF\xD8\xFF"

jpg_prefix.encoding
# => #<Encoding:ASCII-8BIT>

0

你应该能够将文件信息与字符编码进行比较,类似于:

if data[0, 3] == 0xff.chr + 0xd8.chr + 0xff.chr
  puts "This is a JPG"
end

如果你遇到问题,你可以随时查看fastimage gem的代码,类型检测代码在这里:https://github.com/sdsykes/fastimage/blob/master/lib/fastimage.rb#L337-L354

就像其他人(@ Stefan)提到的那样,你原始示例中的字符串不匹配是因为编码不同。

# Check the encodings for our strings:
"\xff\xd8\xff".encoding                   #=> <Encoding:UTF-8>
(0xff.chr + 0xd8.chr + 0xff.chr).encoding #=> <Encoding:ASCII-8BIT>

# Compare our two strings with different encodings:
utf8  = "\xff\xd8\xff"
ascii = 0xff.chr + 0xd8.chr + 0xff.chr

utf8 == ascii                              #=> false
utf8.force_encoding("ASCII-8BIT") == ascii #=> true

如果你强制编码为ASCII-8BIT,你的原始代码实际上可以正常工作。


你的解决方案有效了!能否稍微解释一下它是如何工作的,这样我就可以理解了? - user1360461
Stefan的回答非常好地解释了它,你应该在下面检查他的回答! - Mario Visic

0

如果可以的话,让别人来识别文件是一件好事。ruby-filemagic gem 可以做到这一点。

gem 'ruby-filemagic'

在使用中,它将返回一个字符串:
require 'filemagic'

magic = FileMagic.new
p magic.file("/tmp/pic1.jpg")
# => "JPEG image data, JFIF standard 1.02"

返回的字符串可以与正则表达式匹配:

case magic.file(path)
when /JPEG/
  # do JPEG stuff
when /GIF/
  # do GIF stuff
else
  # we don't recognize it
end

ruby-filemagic使用libmagic库,该库可以识别大量文件类型。

文档有点稀少(自述文件甚至没有“hello world”示例),而且已经几年没有更新了,但不要因此而放弃尝试。它很简单易用,而且非常可靠——我今天正在使用生产代码,它仍然正常工作。

如果由于某种原因您无法使用gem,但是在*nix环境中并且可以访问“file”命令,则可以通过调用“file”来获得相同的功能:

p `file /tmp/pic1.jpg`
# => "/tmp/pic1.jpg: JPEG image data, JFIF standard 1.02\n

在 Debian 中,“file”命令由“file”软件包提供。您的操作系统可能不同。

0

当 Data 是一个字符串时,您的代码对我来说运行良好 - 但是 Data 很可能是一个字节值数组。

尝试这个:

if data[0,3] == [0xff, 0xd8, 0xff]

根据您的条件。


我对Ruby等方面还有点新。我正在使用IO从实际的JPG文件中读取4个字节,并且我认为它是以字符串形式读入十六进制值,但不完全确定。无论如何,数组是个好主意,但是没有起作用 :/ - user1360461
你的字符串可能是使用不同的字符集编码,与你所期望的不一致吗?你是否已经打印出字符串的前三个字节并验证其字节值? - mcfinnigan
我也发现这很痛苦。如果我使用 puts 来显示数据,它总是显示为????。但我确认它确实读取了正确的数据。 - user1360461

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接