如何使用Python区分二进制文件和文本文件?

18

我需要识别一个目录中的文件二进制的还是文本的。

我尝试使用mimetypes,但在我的情况下它并不是一个好办法,因为它不能识别所有文件的mimes,并且我这里有一些奇怪的文件类型... 我只需要知道,二进制还是文本。简单?但我找不到解决方法...

谢谢


2
对你而言,什么是文本文件?例如UTF-16-BE编码的Unicode算吗? - lutz
3
在任何人能够帮助你之前,你需要精确定义“二进制”和“文本”的含义。 - Grzegorz Oledzki
文本文件是任何可被人类阅读的文件。比如说,任何你可以通过“cat”(Linux)或“type”(Windows)命令来读取的文件。 - Thomas
这个类似的问题有一些很好的答案,https://dev59.com/uXNA5IYBdhLWcg3wmfEa file(1)非常可靠,所以你可以选择基于file(1)行为的纯Python解决方案;或者你可以信任mimetypes模块。 - Sam Watkins
使用这个库:https://pypi.python.org/pypi/binaryornot/它非常简单,基于在这个stackoverflow问题中找到的代码。 - guettli
4个回答

11
感谢大家,我找到了解决我的问题的方法。我在http://code.activestate.com/recipes/173220/找到了这段代码,并稍微修改了一下以适应我的需求。
它运行良好。
from __future__ import division
import string 

def istext(filename):
    s=open(filename).read(512)
    text_characters = "".join(map(chr, range(32, 127)) + list("\n\r\t\b"))
    _null_trans = string.maketrans("", "")
    if not s:
        # Empty files are considered text
        return True
    if "\0" in s:
        # Files with null bytes are likely binary
        return False
    # Get the non-text characters (maps a character to itself then
    # use the 'remove' option to get rid of the text characters.)
    t = s.translate(_null_trans, text_characters)
    # If more than 30% non-text characters, then
    # this is considered a binary file
    if float(len(t))/float(len(s)) > 0.30:
        return False
    return True

7
对你的代码稍作修改: if float(len(t))/float(len(s)) > 0.30: return 0 否则,Python会使用整数除法,当len(t) == len(s)时,比较结果只会为真。 - Cédric Julien
1
Thomas,请将“float”修正应用于答案!Activestate也应该修复他们的配方!;)但我懒得注册以提高那里的评论。 - Sam Watkins
1
@cedriv-julien,@sam-watkins,我认为不需要使用float,因为有from __future__ import division这一行,是吗? - simon
4
类型错误:无法对“map”和“list”执行操作符“+”。 - abg
1
这段代码不适用于Python 3。 - Alg_D
显示剩余2条评论

8

这是固有的不简单。虽然在大多数情况下你可以做一个合理的猜测,但无法确定。

以下是您可能想要执行的操作:

  • 查找二进制签名中已知的幻数
  • 查找文件开头处的Unicode字节顺序标记
  • 如果文件经常是00 xx 00 xx 00 xx(对于任意xx)或反之亦然,则很可能是UTF-16
  • 否则,请查找文件中的0;带有0的文件不太可能是单字节编码的文本文件。

但这全是启发式方法 - 可能存在一个既是有效文本文件,是图像文件的文件。它可能作为文本文件是无意义的,但在某些编码中是合法的...


7

5

如果你的脚本运行在*nix系统上,你可以使用类似于这样的代码:

import subprocess
import re

def is_text(fn):
    msg = subprocess.Popen(["file", fn], stdout=subprocess.PIPE).communicate()[0]
    return re.search('text', msg) != None

如果只是查找子字符串,无需使用 re - Steven Lu
如果“text”是二进制文件路径的一部分,则无法正常工作。 - Paddre
2
我建议使用Popen(["file", "--mime", fn] ...)。否则,单词“text”可能不会出现。在我的Linux上,类似Fortran程序的答案是“FORTAN程序”。如果您添加mime开关,则会得到“text/x-fortran; charset=us-ascii”。 - Tsf
如果你正在使用Python 3,msg将是字节而不是字符串,因此你必须使用return re.search("text", msg.decode()) != None 或者 return "text" in msg.decode() - Matt Pitkin

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