【问题标题】:Ruby - Comparing "==" hex value to stringRuby - 将“==”十六进制值与字符串进行比较
【发布时间】:2013-05-24 19:07:59
【问题描述】:

我基本上是在阅读图片文件的标题并进行快速比较以查看它实际上是什么类型的文件。 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

我知道我很接近,但我就是无法让它工作。请让我知道我在这里错过了什么。

注意:我知道有宝石可以为我做这件事,但我不想使用宝石。就这么简单。

【问题讨论】:

  • 您的数据是什么样的?
  • @Anand 我使用 data[0, 3] 来获取我读入的 4 个字节的前 3 个字节

标签: ruby string encoding hex


【解决方案1】:

您应该能够将文件信息与字符代码进行比较,例如:

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,您的原始代码实际上可以正常工作

【讨论】:

  • 您的解决方案奏效了!你能向我解释一下它是如何工作的,以便我理解吗?
  • Stefan 的回答很好地解释了它;你应该在下面查看他的答案!
【解决方案2】:

这是一个字符编码问题。从 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>

【讨论】:

  • 很好的解释。非常感谢
【解决方案3】:

如果可以的话,让其他人来识别文件是一件好事。 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”命令,则可以通过 shell out 到“file”来获得相同的功能:

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

在 Debian 中,file 命令由包 file 提供。您的操作系统可能有所不同。

【讨论】:

    【解决方案4】:

    当 Data 是一个字符串时,您的代码对我来说可以正常工作 - 但 Data 可能是一个字节值数组。

    试试这个:

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

    根据你的情况。

    【讨论】:

    • 我对 ruby​​ 等还是有点陌生​​。我使用 IO 从实际的 JPG 文件中读取 4 个字节,我认为它以字符串形式(?)读取十六进制值,但不完全确定。无论如何,数组是个好主意,但没用:/
    • 您的字符串可能与您期望的字符集编码不同吗?你打印出字符串的前三个字节并验证字节值了吗?
    • 我也发现这很痛苦。如果我使用puts 显示数据,它似乎总是显示为????。我确实确认它正在读取正确的数据
    猜你喜欢
    • 1970-01-01
    • 2021-08-26
    • 1970-01-01
    • 2011-10-16
    • 2021-12-11
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-08-12
    相关资源
    最近更新 更多