9

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.

ここでのポイントは、期待どおりに 1 行として読み取ることです。現在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までの文字は、「インフォメーション・セパレータ・フォー」から「ワン」まで(この順で)記述されている。2 つの点が気になりました: 1) 28 から 30 は行末として解釈され、2) 31 はそうではありません。これは意図した動作ですか?行末として解釈される文字の定義はどこにありますか? それらを行末として解釈しない方法はありますか?

ありがとう。

editで 'UTF-8' 引数をコピーするのを忘れましたcodecs.open。私の質問のコードは修正されました。

4

1 に答える 1

6

これは素晴らしい質問です。

open()またはでファイルを開くかどうかによって違いが生じますcodecs.open()。前者はバイト文字列の観点から動作します。後者は、Unicode 文字列に関して動作します。Python では、これらの動作は異なります。

これと同じ質問が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 制御文字の規則を使用します。その尺度では、バイト 10 と 13 は改行文字です (そして、Python はバイト 13 とそれに続く 10 を単一の改行として扱います)。

しかし、Unicode 文字列の文字が改行であるかどうかを判断するために、Python はUAX #44に記載されているUnicode Character Databaseの文字分類と、UAX #14 Line Breaking Algorithmの section 5 Line Breaking Propertiesの文字分類に従います。Issue 7643 によると、これらのドキュメントは、Python の目的で文字を改行として識別する 3 つの文字プロパティを識別します。

  • 大分類Zl「ラインセパレーター」
  • 一般カテゴリZp「段落区切り」
  • 双方向クラス B「段落区切り」

文字 28 (0x001C)、29 (0x001D)、および 30 (0x001E) には、これらの文字プロパティがあります。文字 31 (0x001F) にはありません。なんで?これは Unicode 技術委員会への質問です。しかし、ASCII では、これらの文字は「ファイル区切り」、「グループ区切り」、「レコード区切り」、および「単位区切り」として知られていました。比較としてタブ付きのテキスト データ ファイルを使用すると、最初の 3 つは少なくとも改行と同じくらいの分離を意味し、4 番目はおそらくタブに類似しています。

これら 3 つの Unicode 文字を Python Unicode 文字列の改行として実際に定義しているコードをObjects/unicodeobject.c. array を探しascii_linebreak[]ます。この配列はunicode.splitlines()の実装の基礎となります。strの基礎となるコードが異なります.splitlines()。私は信じていますが、Pythonソースコードでそれを追跡していません.でenumerate()開かれたファイルはunicodecodecs.open()の観点から実装されています..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.

3.x ではなく Python 2.x を使用していると思います。私の答えは Python 2.7 に基づいています。

于 2013-09-29T09:14:40.697 に答える