2012-02-06 69 views
4

考慮以下(Python的3.2在Windows下):意外行爲用StringIO和CSV模塊

>>> import io 
>>> import csv 
>>> output = io.StringIO()   # default parameter newline=None 
>>> csvdata = [1, 'a', 'Whoa!\nNewlines!'] 
>>> writer = csv.writer(output, quoting=csv.QUOTE_NONNUMERIC) 
>>> writer.writerow(csvdata) 
25 
>>> output.getvalue() 
'1,"a","Whoa!\nNewlines!"\r\n' 

爲什麼會有一個\n - 應該不是一直以來converted to \r\n通用換行模式啓用?

啓用此功能,就輸入,行結尾\n\r,或\r\n 之前被轉換爲\n被返回給調用者。 相反,在輸出,\n被翻譯爲系統默認行 separator,os.linesep

回答

5

「單個」\n作爲第三個字段內的數據字符出現。因此,該字段被引用,以便csv讀者將其視爲數據的一部分。它不是「行終止符」(應該稱爲行分隔符)或其中的一部分。爲了更好地理解報價,請刪除quoting=csv.QUOTE_NONNUMERIC

生成\r\n是因爲csv終止行,其中dialect.lineterminator的默認值爲\r\n。換句話說,「通用換行符」設置被忽略。

更新

2.7和3.2文檔爲io.StringIO實際上是相同的,只要在換行符 ARG而言。

newline參數的工作方式與TextIOWrapper相同。默認值是 ,不進行換行。

我們將檢查下面的第一句。第二句話對於輸出是正確的,取決於你對「默認」和「換行譯文」的解釋。

TextIOWrapper文檔:

換行符可以是無, '', '\ n', '\ r',或 '\ r \ N'。它控制 處理行尾。如果是None,則啓用通用換行符 。啓用此功能後,輸入時,行結束符'\ n','\ r'或 '\ r \ n'在返回給調用者之前被轉換爲'\ n'。 相反,在輸出時,'\ n'被轉換爲系統默認行 分隔符os.linesep。如果換行符是其合法值的任何其他值,則 換行時,換行變爲換行符,並且 返回未翻譯。在輸出時,'\ n'被轉換爲換行符。

Python 3。2在Windows上:

>>> from io import StringIO as S 
>>> import os 
>>> print(repr(os.linesep)) 
'\r\n' 
>>> ss = [S()] + [S(newline=nl) for nl in (None, '', '\n', '\r', '\r\n')] 
>>> for x, s in enumerate(ss): 
...  m = s.write('foo\nbar\rzot\r\n') 
...  v = s.getvalue() 
...  print(x, m, len(v), repr(v)) 
... 
0 13 13 'foo\nbar\rzot\r\n' 
1 13 12 'foo\nbar\nzot\n' 
2 13 13 'foo\nbar\rzot\r\n' 
3 13 13 'foo\nbar\rzot\r\n' 
4 13 13 'foo\rbar\rzot\r\r' 
5 13 15 'foo\r\nbar\rzot\r\r\n' 
>>> 

0行顯示該「默認」你沒有newline ARG得到不涉及的\n翻譯(或任何其他字符)。 這肯定是不轉換'\n'os.linesep

行1顯示了你有newline=None得到(應該是一樣的0線,應該不會吧?)實際上是INPUT通用換行符翻譯 - 離奇!

第2行:newline=''沒有變化,像第0行。它當然不會將'\n'轉換爲''

第3,4和5行:如文檔所述,'\n'轉換爲newline arg的值。

等效的Python 2.X代碼與Python 2.7.2產生相同的結果。

更新2對於內置open(),默認應該是os.linesep,如記錄一致。要獲得無輸出轉換行爲,請使用newline=''。注意:open()文檔更清晰。我明天提交一份錯誤報告。

+0

非常感謝您的詳細解釋(和探索)。我想我在這裏看着一個深淵。 – 2012-02-07 11:15:51

+0

@TimPietzcker:等到你看*輸入*。 – 2012-02-07 11:29:30

2

the docs爲StringIO的:

換行符的說法就像是TextIOWrapper的。默認是不做新行翻譯。

所以StringIO沒有正常進行任何換行。該默認值是有意義的--StringIO不寫入磁盤,所以它不需要轉換爲特定於平臺的換行符。

正如約翰指出的那樣,csv模塊有自己的通用換行符,但僅用於行結尾,不適用於字符串內的換行符。

+0

哎。我一直在使用Python 3,並且正在查看Python 2的文檔... – 2012-02-06 13:33:43

+0

@TimPietzcker:除了'u'foo'而不是''foo'',2.7文檔用於'io.StringIO'和'就'newline' arg而言,io.TextIOWrapper'是相同的。你必須閱讀關於'os.linesep'的TIOW文檔。看到我更新的答案。 – 2012-02-07 10:58:30

+0

@JohnMachin:你說得對。但是,然後Python文檔是不一致的:請參閱我的問題鏈接到和引用的部分。 'TextIOWrapper'的文檔說默認的('newline = None')行爲是*做*翻譯。 'StringIO'的文檔說默認行爲是* not *來做翻譯。困惑。 – 2012-02-07 11:03:50