Python编解码器行结束

Pau*_*aul 7 python unicode encoding line-endings utf-8

似乎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.
Run Code Online (Sandbox Code Playgroud)

这里的要点是它将它读作一行,正如我所期望的那样.现在当我用codecsUTF-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
Run Code Online (Sandbox Code Playgroud)

字符28到31被描述为"信息分隔符四"到"一个"(按此顺序).有两件事让我感动:1)28到30被解释为行结束,2)31不是.这是预期的行为吗?我在哪里可以找到哪些字符被解释为行尾的定义?有没有办法不将它们解释为行尾?

谢谢.

编辑忘了复制'UTF-8'参数codecs.open.我的问题中的代码现已更正.

Jim*_*unt 5

这是一个很好的问题.

无论您是使用open()或打开文件,都会有所不同codecs.open().前者以字节串的形式运行.后者以Unicode字符串的形式运行.在Python中,这些行为有所不同.

同样的问题出现在Python Issue 7643中,什么是Unicode换行符?.讨论以及Unicode字符数据库的引用非常吸引人.问题7643还提供了这个简洁的代码片段来演示差异:

for s in '\x0a\x0d\x1c\x1d\x1e':
  print u'a{}b'.format(s).splitlines(1), 'a{}b'.format(s).splitlines(1)
Run Code Online (Sandbox Code Playgroud)

但归结为此.

为了确定字节字符串中的字节是否是换行符(或空格),Python使用ASCII控制字符的规则.通过该度量,字节10和13是换行符(并且Python将字节13后跟10作为单个换行符).

但是为了确定Unicode字符串中的字符是否是换行符,Python遵循UAX#44记录的Unicode字符数据库的字符分类,以及UAX#14换行算法第5节行属性.根据问题7643,这些文档标识了三个字符属性,这些属性将字符标识为用于Python目的的换行符:

  • 一般类别Zl"线分离器"
  • 一般类别Zp"段落分隔符"
  • 双向B类"段落分离器"

字符28(0x001C),29(0x001D)和30(0x001E)具有这些字符属性.字符31(0x001F)没有.为什么?这是Unicode技术委员会的问题.但在ASCII中,这些字符称为"文件分隔符","组分隔符","记录分隔符"和"单位分隔符".使用选项卡式文本数据文件作为比较,前三个意味着至少与换行符一样多的分离,而第四个可能类似于选项卡.

您可以看到实际将这三个Unicode字符定义为Python Unicode字符串中的换行符的代码Objects/unicodeobject.c.寻找阵列ascii_linebreak[].该数组是unicode.splitlines()实现的基础.不同的代码是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.
Run Code Online (Sandbox Code Playgroud)

我假设您使用的是Python 2.x,而不是3.x. 我的答案基于Python 2.7.