Неожиданное поведение универсального режима новой строки с модулями StringIO и csv

Рассмотрим следующее (Python 3.2 под Windows):

>>> 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 - не было ли преобразовано в \r\n, поскольку включен универсальный режим новых строк?

Когда это разрешено, на входе заканчиваются линии \n, \r или \r\nпереводятся на \n перед возвратом вызывающему. И наоборот, на выходе \n переводится в строку по умолчанию системы разделитель, os.linesep.

Ответ 1

"single" \n происходит как символ данных внутри третьего поля. Следовательно, это поле цитируется так, что csv-ридер будет рассматривать его как часть данных. Это НЕ "ограничитель строки" (следует называть разделителем строк) или его частью. Чтобы получить более высокую оценку цитирования, удалите quoting=csv.QUOTE_NONNUMERIC.

\r\n создается потому, что csv завершает строки с dialect.lineterminator, по умолчанию \r\n. Другими словами, параметр "универсальные новые строки" игнорируется.

Обновить

2.7 и 3.2 docs для io.StringIO практически идентичны по отношению к аргументу newline.

Аргумент новой строки работает так же, как и TextIOWrapper. По умолчанию используется не выполнять перевод новой строки.

Мы рассмотрим первое предложение ниже. Второе предложение верно для вывода, в зависимости от вашей интерпретации "по умолчанию" и "перевода новой строки".

Документы TextIOWrapper:

newline может быть None, '', '\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, не содержит перевода \n (или любого другого символа). Это НЕ преобразование '\n' в os.linesep

Строка 1 показывает, что то, что вы получаете с newline=None (должно быть таким же, как строка 0, не так ли?) действует INPUT универсальный перевод новых строк - причудливый!

Строка 2: newline='' не изменяет, как строка 0. Это, конечно, НЕ преобразует '\n' в ''.

Строки 3, 4 и 5: как говорят документы, '\n' преобразуется в значение аргумента newline.

Эквивалентный код Python 2.X дает эквивалентные результаты с помощью Python 2.7.2.

Обновление 2 Для согласования со встроенным open() по умолчанию должно быть os.linesep, как описано. Чтобы получить поведение без перевода, используйте newline=''. Примечание. Документы open() намного понятнее. Я отправлю отчет об ошибке завтра.

Ответ 2

Из документов для StringIO:

Аргумент новой строки работает так же, как и TextIOWrapper. По умолчанию используется преобразование новой строки.

Итак, StringIO обычно не выполняет никаких переводов на новую строку. Это значение имеет смысл - StringIO не записывает на диск, поэтому ему не нужно переводить на строки новой строки.

Как отметил Джон, модуль csv выполняет свои собственные универсальные строки, но только для строк, а не для строк в строке.