2013-06-24 47 views
7

看來Python的UTF-8編碼(codecs包)解釋Unicode字符28,29,和30行結尾。爲什麼?我怎麼能阻止它這樣做?Python的編解碼器行結束

示例代碼:

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

如果以「rb''模式打開文件,會發生什麼情況? – unutbu

+0

沒有區別。 – Paul

+2

@保羅,你可以回答自己的問題,並接受它,如果你喜歡 –

回答

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性格分類,在UAX #44記錄和UAX #14 Line Breaking Algorithm, section 5 Line Breaking Properties的。據發行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。

+1

謝謝。這是精心製作的。並感謝您的解決方案。說得通。 – Paul

相關問題