似乎Python的UTF-8编码(codecs
包)将Unicode字符28,29和30解释为行结尾。为什么?我怎么能阻止它这样做呢?
示例代码:
with open('unicodetest.txt', 'w') as f:
f.write('a'+chr(28)+'b'+chr(29)+'c'+chr(30)+'d'+chr(31)+'e')
with open('unicodetest.txt', 'r') as f:
for i,l in enumerate(f):
print i, l
# prints "0 abcde" with special characters in between.
这里的要点是它将它读作我预期的一行。现在,当我使用codecs
以UTF-8读取它时,它会将其解释为多行。
import codecs
with codecs.open('unicodetest.txt', 'r', 'UTF-8') as f:
for i,l in enumerate(f):
print i, l
# 0 a
# 1 b
# 2 c
# 3 de
# (again with the special characters after each a, b, c, d
字符28到31被描述为“信息分隔符四”到“一”(按此顺序)。有两件事让我感动:1)28到30被解释为行结束,2)31不是。这是预期的行为吗?我在哪里可以找到哪些字符被解释为行尾的定义?有没有办法不将它们解释为行结束?
感谢。
编辑忘记复制codecs.open
中的'UTF-8'参数。我的问题中的代码现已更正。
答案 0 :(得分:5)
这是一个很好的问题。
使用open()
或codecs.open()
打开文件会有所不同。前者以字节串的形式运行。后者以Unicode字符串的形式运行。在Python中,这些是behave differently。
同样的问题出现在Python Issue 7643, What is a Unicode line break character?。讨论以及对 Unicode Character Database 的引用非常吸引人。问题7643还提供了这个简明的代码片段来演示差异:
for s in '\x0a\x0d\x1c\x1d\x1e':
print u'a{}b'.format(s).splitlines(1), 'a{}b'.format(s).splitlines(1)
但归结为此。
为了确定字节字符串中的字节是换行符(或空格),Python使用ASCII control characters的规则。通过该度量,字节10和13是换行符(并且Python将字节13后跟10作为单个换行符)。
但是为了确定Unicode字符串中的字符是否是换行符,Python遵循Unicode Character Database和{{3}中记录的 UAX #44 的字符分类。 }。根据问题7643,这些文档确定了三个字符属性,这些属性将字符标识为用于Python目的的换行符:
字符28(0x001C),29(0x001D)和30(0x001E)具有这些字符属性。字符31(0x001F)没有。为什么?这是Unicode技术委员会的问题。但在ASCII中,这些字符称为“文件分隔符”,“组分隔符”,“记录分隔符”和“单位分隔符”。使用选项卡式文本数据文件作为比较,前三个意味着至少与换行符一样多的分离,而第四个可能与选项卡类似。
您可以在UAX #14 Line Breaking Algorithm, section 5 Line Breaking Properties中看到实际将这三个Unicode字符定义为Python Unicode字符串中的换行符的代码。查找数组ascii_linebreak[]
。该数组是Objects/unicodeobject.c
的实现的基础。不同的代码是 str .splitlines()
的基础。我相信,但是在Python源代码中没有跟踪它,用enumerate()
打开的文件codecs.open()
是用 unicode .splitlines()
实现的。
你问,“我怎么能阻止它这样做?”我认为没有办法让splitlines()
表现得与众不同。但是,您可以将文件作为字节流打开,使用str.splitlines()
行为将行读取为字节,然后将每行解码为UTF-8以用作unicode字符串:
with open('unicodetest.txt', 'r') as f:
for i,l in enumerate(f):
print i, l.decode('UTF-8')
# prints "0 abcde" with special characters in between.
我假设您使用的是Python 2.x,而不是3.x.我的答案基于Python 2.7。