2012-02-06 64 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