如何判断Python中的流是文本还是二进制?
有没有办法判断一个文件(或者字节流,或者其他类似文件的东西)是文本文件还是二进制文件?就像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'