如何判断Python中的流是文本还是二进制?

2 投票
1 回答
2747 浏览
提问于 2025-04-18 01:58

有没有办法判断一个文件(或者字节流,或者其他类似文件的东西)是文本文件还是二进制文件?就像Unix系统中的file命令那样,在大多数情况下都能准确判断。

动机:虽然应该尽量避免猜测,但如果Python能够判断出这一点,我想利用这个功能。这样可以处理很多常见的情况,同时也能应对一些例外。

我更倾向于跨平台或纯Python的方法。有一种方法是使用python-magic,不过它在Windows上依赖于Cygwin,并且通常还依赖于libmagic

1 个回答

2

来自file命令的手册:

打印出来的类型通常会包含以下几个词:text(文件只包含可打印的字符和一些常见的控制字符,可能在ASCII终端上安全读取)、executable(文件包含编译程序的结果,以某种UNIX内核可以理解的形式存在),或者data,意味着其他任何东西(数据通常是“二进制”或不可打印的)。

既然你只是想判断文件是文本还是二进制,我建议你检查一下流中的每个字符是否都是可打印的。

import string
all(c in string.printable for c in stream)

我觉得你可能永远无法做到100%准确,但这样做应该相对靠谱。不过,你需要处理Unicode编码吗?

编辑 - Unicode支持有点复杂,但如果你有一组可能的编码方式,你可以先测试文档是否能成功从每种编码解码,然后再检查所有字符是否都是可打印的。

import string
import unicodedata

encodings = 'ascii', 'utf-8', 'utf-16'

test_strings = '\xf0\x01\x01\x00\x44', 'this is a test', 'a utf-8 test \xe2\x98\x83'

def attempt_decode(s, encodings):
    for enc in encodings:
        try:
            return s.decode(enc), enc
        except UnicodeDecodeError:
            pass
    return s, 'binary'

def printable(s):
    if isinstance(s, unicode):
        return not any(unicodedata.category(c) in ['Cc'] for c in s)
    return all(c in string.printable for c in s)

for s in test_strings:
    result, enc = attempt_decode(s, encodings)
    if enc != 'binary':
        if not printable(result):
            result, enc = s, 'binary'
    print enc + ' - ' + repr(result)

这样做的结果是:

binary - '\xf0\x01\x01\x00D'
ascii - u'this is a test'
utf-8 - u'a utf-8 test \u2603'

撰写回答