【问题标题】:Unexpected behavior of universal newline mode with StringIO and csv modules使用 StringIO 和 csv 模块的通用换行模式的意外行为
【发布时间】:2012-02-27 18:44:45
【问题描述】:

考虑以下(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 - 既然启用了通用换行模式,它不应该是converted to \r\n吗?

启用此功能后,在 input 上,行结尾为 \n\r\r\n 在返回给调用者之前被翻译为\n。 相反,在 output 上,\n 被转换为系统默认行 分隔符,os.linesep

【问题讨论】:

    标签: python csv python-3.x newline


    【解决方案1】:

    “单个”\n 在第三个字段中作为数据字符出现。因此,该字段被引用,以便 csv 阅读器将其视为数据的一部分。它不是“行终止符”(应称为行分隔符)或其一部分。为了更好地理解引用,请删除quoting=csv.QUOTE_NONNUMERIC

    \r\n 的产生是因为 csv 以 dialect.lineterminator 终止行,dialect.lineterminator 的默认值为 \r\n。换句话说,“通用换行符”设置被忽略了。

    更新

    newline 参数而言,io.StringIO 的 2.7 和 3.2 文档几乎相同。

    换行参数的工作方式与 TextIOWrapper 类似。默认是 不做换行翻译。

    我们将检查下面的第一句话。第二句话对于输出是正确的,取决于你对“默认”和“换行翻译”的解释。

    TextIOWrapper 文档:

    换行符可以是 None、''、'\n'、'\r' 或 '\r\n'。它控制着 处理行尾。如果它是无,通用换行符是 启用。启用此功能后,输入时,行结尾为 '\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'
    >>>
    

    第 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,如文档所述。要获得 no-translation-on-output 行为,请使用 newline=''。注意:open() 文档更加清晰。我明天会提交错误报告。

    【讨论】:

    • 非常感谢您的详细解释(和探索)。我想我正在寻找一个深渊。
    • @TimPietzcker:等你看看input
    【解决方案2】:

    来自the docs for StringIO:

    换行参数的工作方式与 TextIOWrapper 类似。默认是不做换行翻译。

    所以 StringIO 没有正常进行任何换行转换。该默认值是有意义的 - StringIO 不写入磁盘,因此不需要转换为特定于平台的换行符。

    正如 John 所指出的,csv 模块有自己的通用换行符,但仅适用于行结尾,而不适用于字符串中的换行符。

    【讨论】:

    • @TimPietzcker:除了u'foo' 而不是'foo'io.StringIOio.TextIOWrapper 的2.7 文档就newline 参数而言是相同的。您可能已经阅读了关于 os.linesep 的 TIOW 文档。请参阅我的更新答案。
    • @JohnMachin:你是对的。但是 Python 文档不一致:请参阅我在问题中链接和引用的部分。 TextIOWrapper 的文档说默认 (newline=None) 行为是进行翻译。 StringIO 的文档说默认行为是 not 进行翻译。困惑。
    猜你喜欢
    • 1970-01-01
    • 2011-10-07
    • 1970-01-01
    • 1970-01-01
    • 2011-03-09
    • 2021-04-01
    • 2014-03-20
    • 2018-08-02
    • 1970-01-01
    相关资源
    最近更新 更多