有没有一种方法可以检查Ruby变量是否包含二进制数据?

11
我正在使用Ruby 2.4和Rails 5。我有一个名为“content”的变量,其中包含文件内容。该内容可能来自PDF文件、Word文件或HTML文件等。是否有方法可以判断该变量是否包含二进制数据?最终,我想知道这是PDF文件、Microsoft Office文件还是其他类型的OpenOffice文件。此答案--Rails: possible to check if a string is binary? --建议我检查变量的编码。
content.encoding

它将会产生

ASCII-8BIT

然而,在二进制数据的情况下,我注意到变量中存储的HTML内容有时也会返回“ASCII-8BIT”作为content.encoding,因此使用“content.encoding”并不是一种可靠的方法来判断是否存在二进制数据。是否存在其他方法,如果有,是什么?


根据您的要求,看起来您需要对内容进行一些分析。我会提取前n个字节并将它们与标准ASCII代码进行比较。如果您遇到的许多字符不是ASCII,则很可能您的内容是二进制的。似乎卡方检验可能是一个好选择。为什么您无法访问实际的文件对象? - Brennan
我正在访问一个数据库中的内容,该数据库没有关于文件的其他信息。有时会有文件名,但扩展名不可靠,无法确定文件/内容类型。 - Dave
等一下,文件的内容在数据库里? - Brennan
1
@Dave 根据 https://github.com/blackwinter/ruby-filemagic 的宝石文档,它可以使用缓冲区工作,因此您不需要将任何内容写入文件。只需将前N个字节读入内存并传递给宝石即可。 - Brian
这个 gem 能在 Rails 5 上使用吗?我尝试安装它时出现了“Gem::Ext::BuildError: ERROR: Failed to build gem native extension”的错误。 - Dave
显示剩余3条评论
2个回答

3
如果你的真正问题不是关于二进制数据本身而是关于确定数据的文件类型,我建议你看一下ruby-filemagic gem,它可以更可靠地提供这些信息。该gem是libmagic库的简单封装,该库在类Unix系统上是标准的。该库通过扫描文件内容并将其与各种文件类型中已知的“魔法”模式进行匹配来工作。
一个字符串缓冲区的示例用法(例如从数据库读取的数据):
require "ruby-filemagic"

content = File.read("/.../sample.pdf") # just an example to get some data

fm = FileMagic.new
fm.buffer(content)    
#=> "PDF document, version 1.4"

为了使宝石(gem)工作(并编译),您需要在系统上安装file实用程序以及带有标头的magic库。引用自自述文件:

需要file(1)库和标头:

Debian/Ubuntu:+libmagic-dev+
Fedora/SuSE:+file-devel+
Gentoo:+sys-libs/libmagic+
OS X:brew install libmagic

已测试可以在Rails 5下良好运行。

根据您的建议,我没有运行“brew install libmagic”。 运行该命令确实允许安装所有内容。 有一个问题我无法从文档中弄清楚 - “buffer”是否总是以一致的方式打印文件类型?也就是说,Excel文档是否始终输出“Microsoft Excel”,PDF文档是否始终打印单词“PDF”? - Dave
很好!关于你的问题,当然没有绝对的确定性,但我预计输出非常一致。 file 实用程序和相关的 magic 库已经存在很多年了,作者不改变其行为的理由。请查看 源代码,了解库当前识别的所有格式变体。 - Matouš Borák
嗨,我在这个问题上开始了一个赏金,因为我发现这个 gem 打印文件类型的方式没有一致性。我得到了太多的变化,以至于不能放心使用这个解决方案。 - Dave
看一下这个 gem 的源代码,你会发现它基本上只是一个包装器,围绕着系统所识别的东西。所以你的问题在于识别文件的系统工具,而不是 gem 本身。基本上,你已经碰到了计算机中的黑魔法领域之一,也就是很难准确确定文件类型。 - engineerDave
@engineerDave 是的,这就是为什么我建议你查看上面的库源代码。该库可以识别数百种格式及其变体。如果您只需要支持其中几种格式,那么使用这样的库就有意义;如果您需要更通用的东西(例如“所有二进制格式”),那么确实需要使用其他工具。 - Matouš Borák
显示剩余3条评论

0

如果你在Unix机器上,可以使用file命令:

file titi.pdf

你可以像这样做:

然后你可以做一些事情:

require 'open2'

cmd = 'file -'
Open3.popen3(cmd) do |stdin, stdout, wait_thr|
  stdin.write(content)
  stdin.close
  puts "file type is:" + stoud.read
end

我的生产环境是Ubuntu Linux,但我的本地环境是Mac OS X。 - Dave

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