Python食谱-1.11.确定字符串为文本还是二进制编码

原文作者:Andrew Dalke
中文编译:Tony (digitalsatori)

问题

Python的字符串对象既可以保存文本也可以保存任意字节码。如何确定某个字符串保存了何种内容?

解决办法

我可以借鉴Perl的试探法来解决该问题。如果一个字符串包含任何空字符,或者如果其超过30%的字符拥有高位码(大于126的编码)或一些特殊控制字符,那么我们确定此字符串包含二进制编码。

from __future__ import division           # ensure / does NOT truncate
import string
text_characters = "".join(map(chr, range(32, 127))) + "\n\r\t\b"
_null_trans = string.maketrans("", "")
def istext(s, text_characters=text_characters, threshold=0.30):
    # if s contains any null, it's not text:
    if "\0" in s:
        return False
    # an "empty" string is "text" (arbitrary but reasonable choice):
    if not s:
        return True
    # Get the substring of s made up of non-text characters
    t = s.translate(_null_trans, text_characters)
    # s is 'text' if less than 30% of its characters are non-text ones:
    return len(t)/len(s) <= threshold

讨论

你可以很容易的对istext函数作微调,比如传递给他一个指定的值(默认的值为0.30),或者改变text_characters(其值用以说明哪些字符被认作文本,默认的值为所有普通ASCII字符和四个常见的控制字符)。比如我们希望文本字符中包括ISO-8859-1编码的意大利文,我们可以将"àèéìÃ2Ã1"意大利重音符号加入到text_characters。

在大多数情况下,我们需要对文件检测是文本还是字节码。我们可以用istext函数检测文件的开始一段:

def istextfile(filename, blocksize=512, **kwds):
  return istext(open(filename).read(blocksize), **kwds)

对于低于3.0版本的Python, 使用除号操作符 / 对两个整数相除,返回的值为去除小数位的整数值。在以上代码中我们使用了:

from future import division

使我们能使用3.0版本中关于除号操作符的不同特性,即两整数相除返回值可以是浮点数。

2 Responses to “Python食谱-1.11.确定字符串为文本还是二进制编码”

  1. Insion 说道:

    请问如果包括中文的文本,用这个检测还灵不灵?

  2. digitalsatori 说道:

    简单的回答是用这个检测不灵。

    如果仔细看这个函数的构成就能了解作者对“文本“的定义。
    事实上要区分文本还是二进制编码,其本身就是一个伪命题,因为python 2.x中字符串(string)都是二进制编码的(binary code),所谓不同的文本只是以不同编码规则(encoding)所生成二进制码而已。
    作者在这里只是要区分ASCII码编码从32-127的字符和一些控制字符。

Leave a Response