Following system colour scheme - Python 增强提案 Selected dark colour scheme - Python 增强提案 Selected light colour scheme - Python 增强提案

Python 增强提案

PEP 278 – 通用换行符支持

作者:
Jack Jansen <jack at cwi.nl>
状态:
最终
类型:
标准跟踪
创建:
2002年1月14日
Python 版本:
2.3
历史记录:


目录

摘要

本 PEP 讨论了 Python 如何支持对换行格式与平台本地格式不同的文件进行 I/O 操作,使得各个平台上的 Python 都能读取和导入具有 CR(Macintosh)、LF(Unix)或 CR LF(Windows)行结束符的文件。

越来越多地,我们遇到了行结束符与当前平台标准不匹配的文件:从网络下载的文件、远程挂载的不同平台文件系统、使用 Mac 和 Unix 行结束符双重标准的 Mac OS X 等等。

许多工具,如编辑器和编译器,已经优雅地处理了这个问题,如果 Python 也能做到这一点,那就太好了。

规范

通用换行符支持默认启用,但在 Python 配置期间可以禁用。

在启用通用换行符支持的 Python 中,该功能会自动启用,适用于所有 import 语句和 execfile() 调用。对于 eval() 或 exec,没有特殊支持。

在启用通用换行符支持的 Python 中,open() 的模式参数也可以是“U”,表示“以文本文件形式打开以进行输入,并进行通用换行符解释”。出于对称性,模式“rU”也允许使用,类似于“rb”。模式“U”不能与其他模式标志(如“+”)组合使用。输入文件中的任何行结束符在 Python 中都将被视为 '\n',因此很少有其他代码需要更改来处理通用换行符。

换行符转换发生在所有读取数据的调用中:read()readline()readlines() 等等。

没有专门支持以不同的换行符约定输出到文件,因此模式“wU”也是非法的。

以通用换行符模式打开的文件对象将获得一个新的属性“newlines”,它反映了文件中使用的换行符约定。此属性的值之一为 None(尚未读取换行符)、"\r""\n""\r\n" 或包含所有已见换行符类型的元组。

基本原理

通用换行符支持是在 C 中实现的,而不是在 Python 中。这样做是因为我们希望能够导入具有非本地换行符约定的文件,以便 Python Lib 目录可以共享到远程文件系统连接,或者在 Mac OS X 上的 MacPython 和 Unix-Python 之间共享。为了使之可行,通用换行符约定需要对性能的影响尽可能小,这意味着 Python 实现不可行,因为它会拖慢所有导入操作。而且,由于存在具有多种换行符约定的文件,这些文件会由 Visual C++ 和其他 Windows 工具愉快地生成,因此对文件中使用的换行符进行快速检查(如果看到平台本地换行符,则将导入操作交给 C 代码)将不起作用。最后,C 实现还允许轻松处理跟踪回溯等(这些回溯会打开 Python 源代码模块)。

没有通用换行符的输出实现,Python 程序有望自行处理这个问题,或者将文件写入具有平台本地约定的文件。这样做是因为输入是比较难的部分,而将不同的换行符输出到文件在 Python 中已经足够容易了。

而且,令人惊讶的是,输出实现比输入实现要困难得多:许多输出都是通过 PyXXX_Print() 方法完成的,此时文件对象已不再可用,只有 FILE *。因此,输出实现需要以某种方式从 FILE* 转换到文件对象,因为当前换行符分隔符存储在那里。

输入实现没有此类问题:在 Python 源代码树中,没有从 C 部分读取文件、从 Python 部分读取文件的用例,并且这些用例在扩展模块中预计也很少出现。如果存在此类用例,唯一的问题是文件对象的 newlines 属性在 fread()fgets() 调用期间不会更新,这些调用直接从 C 完成。

一个部分输出实现,其中传递给 fp.write() 的字符串将被转换为使用 fp.newlines 作为其行终止符,但所有其他输出都不会,在我看来,这太令人惊讶了。

由于没有对通用换行符的输出支持,因此也不支持模式“rU+”:上一段中的令人惊讶因素将更加强烈。

传递给 eval()exec 的字符串中没有对通用换行符的支持。设想此类字符串始终具有标准的 \n 换行符,如果字符串来自文件,则可以使用通用换行符读取该文件。

我认为 Unicode 没有特殊问题。utf-16 不会造成任何新问题,因为此类文件需要以二进制模式打开。与 utf-8 的交互也很好:值 0x0a 和 0x0d 不会出现在多字节序列中。

通用换行符文件应该与迭代器和 xreadlines() 配合良好,因为这些迭代器最终会调用正常的文件 readline/readlines 方法。

虽然通用换行符在导入时会自动启用,但对于打开操作则不会,你必须显式地指定 open(..., "U")。这可以讨论,但以下是这种设计的一些原因

  • 兼容性。已经对文本文件中 \r\n 进行了自定义解释的程序会中断。此类程序的示例包括在打开具有不同换行符约定的文件时会向你发出警告的编辑器。如果通用换行符被设置为默认值,此类编辑器会默默地将你的行结束符转换为本地约定并保存。在 Unix 上将二进制文件打开为文本文件的程序也会中断(但这可以认为是它们应得的 :-)。
  • 接口清晰度。通用换行符仅支持输入文件,而不支持输入/输出文件,因为语义会变得混乱。如果你所有读取操作都遇到了 Mac 换行符,你会写入 Mac 换行符吗?但如果你后来读取了 Unix 换行符怎么办?

包含 newlines 属性是为了让确实关心换行符约定的程序(如文本编辑器)能够检查文件中包含的内容。然后,它们可以以相同的换行符约定保存(文件副本)(或者,如果是具有混合换行符的文件,则询问用户该怎么做,或者以平台约定输出)。

明确征求对参考实现中的一个项目的反馈:通用换行符例程是否应该获取全局解释器锁。目前它们没有获取,但这可能被认为是在冒险,因为它们可能会修改 FileObject 中的字段。但由于这些例程是 fgets()fread() 的替代品,因此很难决定在调用例程时是否持有锁。此外,唯一的危险是,如果两个线程同时读取同一个 FileObject,可能会看到多余的换行符,或者 newlines 属性可能会无意中被设置为混合状态。我认为,如果你在两个线程中同时读取同一个 FileObject,那么你就是在自找麻烦。

请注意,在 fgets()fread() 替代例程中没有操作全局可访问的指针,只有一些整数值标志,因此出现核心转储的可能性为零(他这么说:-)。

通用换行符支持可以在配置期间禁用,因为它确实会带来轻微的性能损失,而且实现尚未在所有可能的平台上进行测试。它在某些平台上也可能很愚蠢(例如 WinCE 或 Palm 设备)。如果通用换行符支持没有启用,则文件对象不会具有 newlines 属性,因此可以通过以下简单的操作来测试当前 Python 是否具有该属性

if hasattr(open, 'newlines'):
   print 'We have universal newline support'

请注意,此测试使用 open() 函数而不是 file 类型,这样它就不会在 Python 版本中失败,在这些版本中 file 类型不可用(file 类型是在与通用换行符功能相同的版本中添加到内置命名空间中的)。

此外,请注意,此测试在 Python 版本>= 2.5 中再次失败,此时 open() 再次成为函数,不再与 file 类型同义。

参考实现

参考实现可在 SourceForge 补丁 #476814 中获得:https://bugs.python.org/issue476814

参考

无。


来源:https://github.com/python/peps/blob/main/peps/pep-0278.rst

上次修改时间:2023-09-09 17:39:29 GMT