StringIOおよびcsvモジュールによるユニバーサル改行モードの予期しない動作

以下を考慮してください(WindowsではPython 3.2)。

>>> 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 が1つしかないのですか? \ r \ n に変換しましたか?

\ n \ r 、または \ r \ n コード>   呼び出し元に返される前に \ n に変換されます。   逆に、出力 \ n はシステムのデフォルト行に変換されます   セパレータ、 os.linesep

4

2 答え

"single" \ n は、第3フィールドのデータ文字として発生します。その結果、フィールドは引用され、csvリーダーはそれをデータの一部として扱います。これは "行終端文字"(行区切り記号と呼ばれる)またはその一部ではありません。クォートをよりよく理解するには、 quoting = csv.QUOTE_NONNUMERIC を削除してください。

csvはデフォルトで \ r \ n dialect.lineterminator で行を終了するため、 \ r \ n が生成されます。つまり、 "universal newlines"設定は無視されます。

Update

io.StringIO の2.7と3.2のドキュメントは、改行 argに関する限り実質的に同じです。

改行引数はTextIOWrapperと同じように動作します。デフォルトは   改行を行わないようにします。

以下の最初の文を調べます。 "default"と "newline translation"の解釈に応じて、2番目の文は出力にtrueです。

TextIOWrapperドキュメント:

改行はNone、 ''、 '\ n'、 '\ r'、 '\ r \ n'のいずれかです。それは   行末の処理。 Noneの場合、ユニバーサル改行は   有効になりました。これを有効にすると、入力時に '\ n'、 '\ r'、または   '\ r \ n'は、呼び出し元に返される前に '\ n'に変換されます。   逆に、出力では、 '\ n'はシステムのデフォルト行に変換されます   セパレータ、os.linesep。改行がその正当な値の他のものである場合、   その改行は、ファイルが読み込まれたときに改行になります。   翻訳されずに返されます。出力時に、 '\ n'は改行に変換されます。

Windows上のPython 3.2:

>>> 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'
>>>

Line 0 shows that the "default" that you get with no newline arg involves no translation of \n (or any other character). It is certainly NOT converting '\n' to os.linesep

行1は、 newline = None (行0と同じでなければなりませんか?)が有効であることを示しています。 INPUT 奇妙な!

行2: '\ n' "に変換していないのは確かです。

3行目、4行目、5行目:ドキュメントが示すように、 '\ n' newline argの値に変換されます。

同等のPython 2.Xコードは、Python 2.7.2で同等の結果をもたらします。

Update 2 For consistency with built-in open(), the default should be os.linesep, as documented. To get the no-translation-on-output behaviour, use newline=''. Note: the open() docs are much clearer. I'll submit a bug report tomorrow.

5
追加された
詳細な説明(と探検)に感謝します。私はここに深淵を見ていると思う。
追加された 著者 Tim Pietzcker,
@TimPietzcker:入力を見るまで待ってください。
追加された 著者 John Machin,

StringIOのドキュメントから:

改行引数はTextIOWrapperと同じように動作します。デフォルトでは、改行の翻訳を行いません。

だからStringIOは改行を通常行っていません。そのデフォルトは理にかなっています - StringIOはディスクに書き込んでいないので、プラットフォーム固有の改行に変換する必要はありません。

Johnが指摘したように、csvモジュールはユニバーサル改行を行いますが、行末のみであり、文字列内の改行ではありません。

2
追加された
ああ。私はPython 3を使っていましたが、Python 2のドキュメントを見ていました...
追加された 著者 Tim Pietzcker,
@ジョンマーチン:あなたは正しいです。しかし、Pythonのドキュメントは矛盾しています。私がリンクし、私の質問で引用した部分を参照してください。 TextIOWrapper のドキュメントでは、デフォルト( newline = None )の動作は翻訳を行うと言います。 StringIO のドキュメントでは、デフォルト動作は翻訳を行う ではないと言います。困惑した。
追加された 著者 Tim Pietzcker,
@TimPietzcker: 'foo' の代わりに io.StringIO io.TextIOWrapperの2.7ドキュメントは、改行 argに関する限り同一です。あなたは os.linesep に関するTIOW文書を読んでいるはずです。私の更新された答えを見てください。
追加された 著者 John Machin,
@TimPietzcker:更新された回答を読んでください。
追加された 著者 John Machin,