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

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

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

4

4 回答 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连接使用(由 Mario Visic 建议)创建的 ASCII 字符也可以:

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>
于 2013-05-29T14:09:06.993 回答
0

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

试试这个:

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

作为你的条件。

于 2013-05-29T13:37:26.400 回答
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,您的原始代码实际上会正常工作

于 2013-05-29T13:38:06.963 回答
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”命令,则可以通过 shell out 到“file”来获得相同的功能:

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

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

于 2013-05-29T14:37:13.400 回答