Python 编解码器行结尾

标签 python unicode encoding utf-8 line-endings

似乎 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 不是。这是有意的行为吗?我在哪里可以找到哪些字符被解释为行尾的定义?有没有办法不将它们解释为行结束?

谢谢。

edit 忘记复制 codecs.open 中的“UTF-8”参数。我问题中的代码现已更正。

最佳答案

这是一个很好的问题。

使用 open()codecs.open() 打开文件会有所不同。前者根据字节串​​进行操作。后者根据 Unicode 字符串进行操作。在 Python 中,这些 behave differently .

同样的问题出现在 Python Issue 7643, What is a Unicode line break character? . 的讨论和引用 Unicode Character Database ,令人着迷。 Issue 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 的字符分类。 ,记录在 UAX #44 , 和 UAX #14 Line Breaking Algorithm, section 5 Line Breaking Properties .根据 Issue 7643,这些文档确定了三个字符属性,这些属性将一个字符标识为 Python 的换行符:

  • 通用类别 Zl“行分隔符”
  • 通用类别 Zp“段落分隔符”
  • 双向 B 类“段落分隔符”

字符 28 (0x001C)、29 (0x001D) 和 30 (0x001E) 具有这些字符属性。字符 31 (0x001F) 没有。为什么?这是 Unicode 技术委员会的问题。但在 ASCII 中,这些字符被称为“文件分隔符”、“组分隔符”、“记录分隔符”和“单元分隔符”。使用带制表符的文本数据文件作为比较,前三个表示至少与换行符一样多的分隔,而第四个可能类似于制表符。

您可以在 Objects/unicodeobject.c 中看到将这三个 Unicode 字符实际定义为 Python Unicode 字符串中的换行符的代码。 .查找数组 ascii_linebreak[]。该数组是 unicode.splitlines() 实现的基础. str.splitlines() 的代码不同。我相信,但没有在 Python 源代码中追踪它,用 codecs.open() 打开的文件上的 enumerate() 是根据 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。

关于Python 编解码器行结尾,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/17273598/

相关文章:

python - 如何使用Python 3.4通过代理访问HTTPS网页

python - 有没有办法让脚本等到文本文件发生变化?

c++ - VS2013 和 Unicode 文字给出警告

Unicode、UTF、ASCII、ANSI 格式差异

.net - 不在字符集中的 HTML 编码字符

mysql - 呸!我在 ruby​​ 1.9.2 的 Rails 中不断收到此编码错误

python - 是否需要在 scikit-learn 中对训练数据进行编码的输入进行编码?

python随机二进制列表,均匀分布

css - OL-LI 列出 Unicode 中的数字

python - 根据字典替换列中的值 (Pandas)