Following system colour scheme Selected dark colour scheme Selected light colour scheme

Python 增强提案

PEP 100 – Python Unicode 集成

作者:
Marc-André Lemburg <mal at lemburg.com>
状态:
最终版
类型:
标准跟踪
创建:
2000年3月10日
Python 版本:
2.0
历史记录:


目录

历史说明

本文档最初由 Marc-Andre 在 PEP 出现之前撰写,最初作为 Python 分发版中的 Misc/unicode.txt 分发,直到 Python 2.1 版本(包括 2.1 版本)。该文档在该位置的最后修订版本标记为 1.7 版(CVS 修订版 3.10)。由于该文档在 PEP 时代之后显然起到了信息性 PEP 的作用,因此已将其移至此处并重新格式化以符合 PEP 指南。将来将对此文档进行修订,而 Misc/unicode.txt 将包含指向此 PEP 的指针。

-Barry Warsaw,PEP 编辑

简介

本提案的目的是以尽可能简单的方式向 Python 添加原生 Unicode 3.0 支持,而不会在过程中引入太多陷阱。

由于这个目标不容易实现——字符串是 Python 中最基本的对象之一——我们预计本提案将经历一些重大的改进。

请注意,本提案的当前版本由于 Unicode-Python 集成的许多不同方面,仍然有点杂乱无章。

本文档的最新版本始终可从以下地址获取:http://starship.python.net/~lemburg/unicode-proposal.txt

较旧的版本可从以下地址获取:http://starship.python.net/~lemburg/unicode-proposal-X.X.txt

[编者注:新修订应针对此 PEP 文档进行,而 1.7 版之前的历史记录应从 MAL 的网址或 Misc/unicode.txt 中检索]

约定

  • 在示例中,我们使用 u = Unicode 对象和 s = Python 字符串
  • “XXX”标记表示讨论点 (POD)

一般说明

  • Unicode 编码名称在输出时应为小写,在输入时应不区分大小写(所有接受编码名称作为输入的 API 都将其转换为小写)。
  • 编码名称应遵循 Unicode Consortium 使用的命名约定:空格转换为连字符,例如“utf 16”写成“utf-16”。
  • 编解码器模块应使用相同的名称,但将连字符转换为下划线,例如 utf_8utf_16iso_8859_1

Unicode 默认编码

Unicode 实现必须对传递给它的用于强制转换的 8 位字符串的编码以及在未给出特定编码时用于将 Unicode 转换为字符串的默认转换编码做出一些假设。此编码在本文中称为<default encoding>。

为此,实现维护一个全局变量,可以在 site.py Python 启动脚本中设置该全局变量。后续更改是不可能的。可以使用 sys 模块的两个 API 设置和查询<default encoding>

sys.setdefaultencoding(encoding)
设置 Unicode 实现使用的<default encoding>。encoding 必须是 Python 安装支持的编码,否则将引发 LookupError。

注意:此 API 仅在 site.py 中可用!site.py 在使用后将其从 sys 模块中移除。

sys.getdefaultencoding()
返回当前<default encoding>。

如果未定义或设置,则<default encoding>默认为“ascii”。此编码也是 Python 的启动默认值(并在执行 site.py 之前生效)。

请注意,默认的 site.py 启动模块包含已禁用的可选代码,该代码可以根据当前区域设置定义的编码设置<default encoding>。locale 模块用于从操作系统环境中定义的区域设置默认设置中提取编码(请参阅 locale.py)。如果无法确定编码、未知或不受支持,则代码默认为将<default encoding>设置为“ascii”。要启用此代码,请编辑 site.py 文件或将相应的代码放入 Python 安装的 sitecustomize.py 模块中。

Unicode 构造函数

Python 应提供一个用于 Unicode 字符串的内置构造函数,该构造函数可通过 __builtins__ 使用

u = unicode(encoded_string[,encoding=<default encoding>][,errors="strict"])

u = u'<unicode-escape encoded Python string>'

u = ur'<raw-unicode-escape encoded Python string>'

将“unicode-escape”编码定义为

  • 所有非转义字符都表示自身作为 Unicode 序数(例如,“a” -> U+0061)。
  • 所有现有的已定义 Python 转义序列都解释为 Unicode 序数;请注意,\xXXXX 可以表示所有 Unicode 序数,而 \OOO(八进制)可以表示最多为 U+01FF 的 Unicode 序数。
  • 一个新的转义序列,\uXXXX,表示 U+XXXX;在 \u 后面少于 4 位数字都是语法错误。

有关错误可能值的说明,请参阅下面的编解码器部分。

示例

u'abc'          -> U+0061 U+0062 U+0063
u'\u1234'       -> U+1234
u'abc\u1234\n'  -> U+0061 U+0062 U+0063 U+1234 U+005c

“raw-unicode-escape”编码定义如下

  • \uXXXX 序列仅当前导反斜杠的数量为奇数时才表示 U+XXXX Unicode 字符
  • 所有其他字符都表示自身作为 Unicode 序数(例如,“b” -> U+0062)

请注意,您应该在源文件的前几行注释中提供一些有关用于编写程序的编码的提示(例如,“# source file encoding: latin-1”)。如果您只使用 7 位 ASCII,则一切正常,不需要此类通知,但如果您包含 ASCII 中未定义的 Latin-1 字符,则可能值得包含提示,因为其他国家/地区的人们也希望能够阅读您的源字符串。

Unicode 类型对象

Unicode 对象应具有类型 UnicodeType,类型名称为“unicode”,可通过标准类型模块使用。

Unicode 输出

Unicode 对象具有一个方法 .encode([encoding=<default encoding>]),该方法返回一个 Python 字符串,使用给定的方案对 Unicode 字符串进行编码(请参阅编解码器)。

print u := print u.encode()   # using the <default encoding>

str(u)  := u.encode()         # using the <default encoding>

repr(u) := "u%s" % repr(u.encode('unicode-escape'))

另请参阅内部参数解析和缓冲区接口,了解用 C 编写的其他 API 如何处理 Unicode 对象。

Unicode 序数

由于 Unicode 3.0 具有 32 位序数字符集,因此实现应提供 32 位感知序数转换 API

ord(u[:1]) (this is the standard ord() extended to work with Unicode
            objects)
  --> Unicode ordinal number (32-bit)

unichr(i)
    --> Unicode object for character i (provided it is 32-bit);
        ValueError otherwise

这两个 API 都应进入 __builtins__,就像它们的字符串对应项 ord()chr() 一样。

请注意,Unicode 为私有编码提供了空间。使用这些编码可能会导致不同机器上的输出表示不同。此问题不是 Python 或 Unicode 问题,而是机器设置和维护问题。

比较与哈希值

Unicode 对象应与其他对象比较相等,在这些其他对象强制转换为 Unicode 之后。对于字符串,这意味着它们被解释为使用<default encoding>的 Unicode 字符串。

Unicode 字符串应返回与其 ASCII 等效字符串相同的哈希值。包含非 ASCII 值的 Unicode 字符串不能保证返回与其默认编码的等效字符串表示相同的哈希值。

当使用 cmp()(或 PyObject_Compare())进行比较时,实现应屏蔽在转换过程中引发的 TypeErrors,以保持与字符串行为的同步。在强制转换字符串为 Unicode 期间引发的所有其他错误(如 ValueErrors)都不应被屏蔽,而应传递给用户。

在包含测试中(“a” in u’abc’ 和 u’a’ in ‘abc’),应在应用测试之前将双方都强制转换为 Unicode。在强制转换过程中发生的错误(例如 None in u’abc’)不应被屏蔽。

强制转换

使用 Python 字符串和 Unicode 对象来形成新对象应始终强制转换为更精确的格式,即 Unicode 对象。

u + s := u + unicode(s)

s + u := unicode(s) + u

所有字符串方法都应将调用委托给等效的 Unicode 对象方法调用,方法是将所有相关的字符串转换为 Unicode,然后将参数应用于同名的 Unicode 方法,例如

string.join((s,u),sep) := (s + sep) + u

sep.join((s,u)) := (s + sep) + u

有关 %-格式化与 Unicode 对象的关系,请参阅格式化标记。

异常

UnicodeError 在 exceptions 模块中定义为 ValueError 的子类。它可在 C 级别通过 PyExc_UnicodeError 使用。所有与 Unicode 编码/解码相关的异常都应为 UnicodeError 的子类。

编解码器 (编码器/解码器) 查找

编解码器 (请参阅编解码器接口定义) 搜索注册表应由“codecs”模块实现

codecs.register(search_function)

搜索函数应采用一个参数,即所有小写字母以及连字符和空格都转换为下划线的编码名称,并返回一个函数元组 (encoder、decoder、stream_reader、stream_writer),这些函数采用以下参数

encoder 和 decoder
这些必须是函数或方法,其接口与编解码器实例的 .encode/.decode 方法相同(请参阅编解码器接口)。这些函数/方法应以无状态模式工作。
stream_reader 和 stream_writer
这些需要是具有以下接口的工厂函数
factory(stream,errors='strict')

工厂函数必须返回提供 StreamWriter/StreamReader 分别定义的接口的对象(请参阅编解码器接口)。流编解码器可以维护状态。

错误的可能值在下面的编解码器部分定义。

如果搜索函数找不到给定的编码,则应返回 None。

编码的别名支持留给搜索函数来实现。

出于性能考虑,codecs 模块将维护一个编码缓存。首先在缓存中查找编码。如果未找到,则扫描注册的搜索函数列表。如果未找到 codecs 元组,则引发 LookupError。否则,将 codecs 元组存储在缓存中并返回给调用方。

要查询 Codec 实例,应使用以下 API

codecs.lookup(encoding)

这将返回找到的 codecs 元组或引发 LookupError

标准编解码器

标准 codecs 应位于标准 Python 代码库中的 encodings/ 包目录中。该目录的 __init__.py 文件应包含一个兼容 Codec Lookup 的搜索函数,该函数实现基于延迟模块的 codec 查找。

Python 应该为最相关的编码提供一些标准 codecs,例如:

'utf-8':              8-bit variable length encoding
'utf-16':             16-bit variable length encoding (little/big endian)
'utf-16-le':          utf-16 but explicitly little endian
'utf-16-be':          utf-16 but explicitly big endian
'ascii':              7-bit ASCII codepage
'iso-8859-1':         ISO 8859-1 (Latin 1) codepage
'unicode-escape':     See Unicode Constructors for a definition
'raw-unicode-escape': See Unicode Constructors for a definition
'native':             Dump of the Internal Format used by Python

还应默认提供常见的别名,例如“latin-1”用于“iso-8859-1”。

注意:“utf-16”应通过使用并要求字节顺序标记 (BOM) 来实现文件输入/输出。

所有其他编码(例如支持亚洲脚本的 CJK 编码)都应在单独的包中实现,这些包不包含在核心 Python 发行版中,也不属于本提案的一部分。

编解码器接口定义

以下基类应在“codecs”模块中定义。它们不仅为编码模块实现者提供模板,还定义了 Unicode 实现期望的接口。

请注意,此处定义的 Codec 接口非常适合更广泛的应用。Unicode 实现期望 .encode().write() 的输入为 Unicode 对象,.decode() 的输入为与字符缓冲区兼容的对象。.encode().read() 的输出应为 Python 字符串,.decode() 必须返回一个 Unicode 对象。

首先,我们有无状态编码器/解码器。它们不像流 codecs(见下文)那样分块工作,因为所有组件都应该在内存中可用。

class Codec:

    """Defines the interface for stateless encoders/decoders.

       The .encode()/.decode() methods may implement different
       error handling schemes by providing the errors argument.
       These string values are defined:

         'strict'  - raise an error (or a subclass)
         'ignore'  - ignore the character and continue with the next
         'replace' - replace with a suitable replacement character;
                     Python will use the official U+FFFD
                     REPLACEMENT CHARACTER for the builtin Unicode
                     codecs.
    """

    def encode(self,input,errors='strict'):

        """Encodes the object input and returns a tuple (output
           object, length consumed).

           errors defines the error handling to apply.  It
           defaults to 'strict' handling.

           The method may not store state in the Codec instance.
           Use StreamCodec for codecs which have to keep state in
           order to make encoding/decoding efficient.
        """

    def decode(self,input,errors='strict'):

        """Decodes the object input and returns a tuple (output
           object, length consumed).

           input must be an object which provides the
           bf_getreadbuf buffer slot.  Python strings, buffer
           objects and memory mapped files are examples of objects
           providing this slot.

           errors defines the error handling to apply.  It
           defaults to 'strict' handling.

           The method may not store state in the Codec instance.
           Use StreamCodec for codecs which have to keep state in
           order to make encoding/decoding efficient.

        """

StreamWriterStreamReader 定义了用于有状态编码器/解码器的接口,这些编码器/解码器在流上工作。这些允许分块处理数据以有效地使用内存。如果内存中有大型字符串,您可能希望用 cStringIO 对象包装它们,然后在它们上面使用这些 codecs 来进行分块处理,例如向用户提供进度信息。

class StreamWriter(Codec):

    def __init__(self,stream,errors='strict'):

        """Creates a StreamWriter instance.

           stream must be a file-like object open for writing
           (binary) data.

           The StreamWriter may implement different error handling
           schemes by providing the errors keyword argument.
           These parameters are defined:

             'strict' - raise a ValueError (or a subclass)
             'ignore' - ignore the character and continue with the next
             'replace'- replace with a suitable replacement character
        """
        self.stream = stream
        self.errors = errors

    def write(self,object):

        """Writes the object's contents encoded to self.stream.
        """
        data, consumed = self.encode(object,self.errors)
        self.stream.write(data)

    def writelines(self, list):

        """Writes the concatenated list of strings to the stream
           using .write().
        """
        self.write(''.join(list))

    def reset(self):

        """Flushes and resets the codec buffers used for keeping state.

           Calling this method should ensure that the data on the
           output is put into a clean state, that allows appending
           of new fresh data without having to rescan the whole
           stream to recover state.
        """
        pass

    def __getattr__(self,name, getattr=getattr):

        """Inherit all other methods from the underlying stream.
        """
        return getattr(self.stream,name)


class StreamReader(Codec):

    def __init__(self,stream,errors='strict'):

        """Creates a StreamReader instance.

           stream must be a file-like object open for reading
           (binary) data.

           The StreamReader may implement different error handling
           schemes by providing the errors keyword argument.
           These parameters are defined:

             'strict' - raise a ValueError (or a subclass)
             'ignore' - ignore the character and continue with the next
             'replace'- replace with a suitable replacement character;
        """
        self.stream = stream
        self.errors = errors

    def read(self,size=-1):

        """Decodes data from the stream self.stream and returns the
           resulting object.

           size indicates the approximate maximum number of bytes
           to read from the stream for decoding purposes.  The
           decoder can modify this setting as appropriate.  The
           default value -1 indicates to read and decode as much
           as possible.  size is intended to prevent having to
           decode huge files in one step.

           The method should use a greedy read strategy meaning
           that it should read as much data as is allowed within
           the definition of the encoding and the given size, e.g.
           if optional encoding endings or state markers are
           available on the stream, these should be read too.
        """
        # Unsliced reading:
        if size < 0:
            return self.decode(self.stream.read())[0]

        # Sliced reading:
        read = self.stream.read
        decode = self.decode
        data = read(size)
        i = 0
        while 1:
            try:
                object, decodedbytes = decode(data)
            except ValueError,why:
                # This method is slow but should work under pretty
                # much all conditions; at most 10 tries are made
                i = i + 1
                newdata = read(1)
                if not newdata or i > 10:
                    raise
                data = data + newdata
            else:
                return object

    def readline(self, size=None):

        """Read one line from the input stream and return the
           decoded data.

           Note: Unlike the .readlines() method, this method
           inherits the line breaking knowledge from the
           underlying stream's .readline() method -- there is
           currently no support for line breaking using the codec
           decoder due to lack of line buffering.  Subclasses
           should however, if possible, try to implement this
           method using their own knowledge of line breaking.

           size, if given, is passed as size argument to the
           stream's .readline() method.
        """
        if size is None:
            line = self.stream.readline()
        else:
            line = self.stream.readline(size)
        return self.decode(line)[0]

    def readlines(self, sizehint=0):

        """Read all lines available on the input stream
           and return them as list of lines.

           Line breaks are implemented using the codec's decoder
           method and are included in the list entries.

           sizehint, if given, is passed as size argument to the
           stream's .read() method.
        """
        if sizehint is None:
            data = self.stream.read()
        else:
            data = self.stream.read(sizehint)
        return self.decode(data)[0].splitlines(1)

    def reset(self):

        """Resets the codec buffers used for keeping state.

           Note that no stream repositioning should take place.
           This method is primarily intended to be able to recover
           from decoding errors.

        """
        pass

    def __getattr__(self,name, getattr=getattr):

        """ Inherit all other methods from the underlying stream.
        """
        return getattr(self.stream,name)

流 codec 实现者可以自由地将 StreamWriterStreamReader 接口组合到一个类中。甚至可以将所有这些与 Codec 类组合在一起。

实现者可以自由地添加其他方法来增强 codec 功能或提供他们工作所需的额外状态信息。但是,内部 codec 实现只会使用上述接口。

Unicode 实现不需要使用这些基类,只需要接口匹配即可;这允许将 Codecs 编写为扩展类型。

作为指导原则,大型映射表应使用静态 C 数据在单独的(共享)扩展模块中实现。这样,多个进程可以共享相同的数据。

应提供一个工具来自动将 Unicode 映射文件转换为映射模块,以简化对其他映射的支持(请参阅参考文献)。

空白字符

.split() 方法必须知道在 Unicode 中什么被认为是空白字符。

大小写转换

Unicode 数据的大小写转换相当复杂,因为需要遵守许多不同的条件。参见

了解有关实现大小写转换的一些指南。

对于 Python,我们应该只实现 Unicode 中包含的 1-1 转换。依赖于区域设置和其他特殊情况的转换(请参阅 Unicode 标准文件 SpecialCasing.txt)应留给用户级例程,而不应进入核心解释器。

.capitalize().iscapitalized() 方法应尽可能遵循上述技术报告中定义的大小写映射算法。

换行符

应为所有具有 B 属性的 Unicode 字符以及组合 CRLF、CR、LF(按此顺序解释)和其他标准定义的特殊换行符执行换行。

Unicode 类型应提供 .splitlines() 方法,该方法根据上述规范返回一个行列表。请参阅 Unicode 方法。

Unicode 字符属性

单独的模块“unicodedata”应为标准的 UnicodeData.txt 文件中定义的所有 Unicode 字符属性提供一个紧凑的接口。

除其他事项外,这些属性提供了识别数字、数字、空格、空白等的方法。

由于此模块必须提供对所有 Unicode 字符的访问,因此最终必须包含来自 UnicodeData.txt 的数据,这些数据大约占用 600kB。因此,数据应存储在静态 C 数据中。这使得可以作为共享模块进行编译,底层操作系统可以在进程之间共享该模块(与普通的 Python 代码模块不同)。

应该有一个标准的 Python 接口来访问这些信息,以便其他实现者可以插入他们自己的可能增强的版本,例如那些动态解压缩数据的版本。

私有代码点区域

对这些的支持留给用户级 Codecs,而不是明确集成到核心代码中。请注意,由于实现了内部格式,因此只有 \uE000\uF8FF 之间的区域可用于私有编码。

内部格式

Unicode 对象的内部格式应使用 Python 特定的固定格式 <PythonUnicode> 实现为“unsigned short”(或其他具有 16 位的无符号数值类型)。字节顺序取决于平台。

此格式将保存相应 Unicode 序数值的 UTF-16 编码。Python Unicode 实现将像处理 UCS-2 值一样处理这些值。对于所有当前定义的 Unicode 字符点,UCS-2 和 UTF-16 都是相同的。不带代理的 UTF-16 可以访问大约 64k 个字符,并涵盖 Unicode 的基本多语言平面 (BMP) 中的所有字符。

Codec 负责确保它们传递给 Unicode 对象构造函数的数据符合此假设。构造函数不会检查数据是否符合 Unicode 或使用代理。

未来的实现可以将 32 位限制扩展到所有 UTF-16 可寻址字符的完整集合(大约 1M 个字符)。

Unicode API 应提供从 <PythonUnicode> 到编译器的 wchar_t 的接口例程,这可以是 16 位或 32 位,具体取决于所使用的编译器/libc/平台。

Unicode 对象应包含一个指向缓存的 Python 字符串对象 <defenc> 的指针,该对象使用 <default encoding> 保存对象的 value。这对于性能和内部解析(请参阅内部参数解析)原因是必要的。当第一次对对象发出转换为 <default encoding> 的请求时,缓冲区将被填充。

目前不需要进行内部化,因为 Python 标识符被定义为仅为 ASCII。

codecs.BOM 应返回内部使用的格式的字节顺序标记 (BOM)。codecs 模块应为了方便和参考提供以下其他常量(codecs.BOM 将是 BOM_BEBOM_LE,具体取决于平台)

BOM_BE: '\376\377'
  (corresponds to Unicode U+0000FEFF in UTF-16 on big endian
   platforms == ZERO WIDTH NO-BREAK SPACE)

BOM_LE: '\377\376'
  (corresponds to Unicode U+0000FFFE in UTF-16 on little endian
   platforms == defined as being an illegal Unicode character)

BOM4_BE: '\000\000\376\377'
  (corresponds to Unicode U+0000FEFF in UCS-4)

BOM4_LE: '\377\376\000\000'
  (corresponds to Unicode U+0000FFFE in UCS-4)

请注意,Unicode 将大端字节顺序视为“正确”。交换后的顺序被视为“错误”格式的指示,因此是非法字符定义。

配置脚本应提供帮助,以确定 Python 是否可以使用本机 wchar_t 类型(它必须是 16 位无符号类型)。

缓冲区接口

使用 <defenc> Python 字符串对象作为 bf_getcharbuf 的基础,以及使用内部缓冲区作为 bf_getreadbuf 的基础来实现缓冲区接口。如果请求 bf_getcharbuf 并且 <defenc> 对象尚不存在,则首先创建它。

请注意,作为特例,解析器标记“s#”不会返回原始 Unicode UTF-16 数据(bf_getreadbuf 返回的数据),而是尝试使用默认编码对 Unicode 对象进行编码,然后返回指向结果字符串对象的指针(或者在转换失败的情况下引发异常)。这样做是为了防止意外地将二进制数据写入输出流,而另一端可能无法识别这些数据。

这具有能够写入输出流(通常使用此接口)而无需额外指定要使用的编码的优点。

如果需要访问 Unicode 对象的读取缓冲区接口,请使用 PyObject_AsReadBuffer() 接口。

还可以使用“unicode-internal”codec 访问内部格式,例如通过 u.encode('unicode-internal')

Pickle/序列化

应具有本机 Unicode 对象支持。对象应使用平台无关的编码进行编码。

Marshal 应使用 UTF-8,Pickle 应选择 Raw-Unicode-Escape(在文本模式下)或 UTF-8(在二进制模式下)作为编码。使用 UTF-8 而不是 UTF-16 的优点是可以消除存储 BOM 标记的需要。

正则表达式

Secret Labs AB 正在开发一个支持 Unicode 的正则表达式机制。它适用于普通的 8 位、UCS-2 和(可选)UCS-4 内部字符缓冲区。

另请参阅

了解有关如何处理 Unicode RE 的一些说明。

格式化标记

格式标记用于 Python 格式字符串中。如果将 Python 字符串用作格式字符串,则应采用以下解释

'%s': For Unicode objects this will cause coercion of the
      whole format string to Unicode.  Note that you should use
      a Unicode format string to start with for performance
      reasons.

如果格式字符串是 Unicode 对象,则所有参数首先强制转换为 Unicode,然后组合在一起并根据格式字符串进行格式化。数字首先转换为字符串,然后转换为 Unicode。

'%s': Python strings are interpreted as Unicode
      string using the <default encoding>.  Unicode objects are
      taken as is.

所有其他字符串格式化程序都应相应地工作。

示例

u"%s %s" % (u"abc", "abc")  ==  u"abc abc"

内部参数解析

这些标记由 PyArg_ParseTuple() API 使用

“U”
检查 Unicode 对象并返回指向它的指针
“s”
对于 Unicode 对象:返回指向对象的 <defenc> 缓冲区的指针(使用 <default encoding>)。
“s#”

访问 Unicode 对象的默认编码版本(参见缓冲区接口);请注意,长度与默认编码字符串的长度相关,而不是 Unicode 对象的长度。
“t#”
与“s#”相同。
“es”
接受两个参数:编码(const char *)和缓冲区(char **)。

输入对象首先以通常的方式强制转换为 Unicode,然后使用给定的编码将其编码为字符串。

在输出时,会分配一个所需大小的缓冲区,并通过 *buffer 作为以 NULL 结尾的字符串返回。编码可能不包含嵌入的 NULL 字符。调用方负责在使用后调用 PyMem_Free() 来释放分配的 *buffer

“es#”
接受三个参数:编码(const char *)、缓冲区(char **)和缓冲区长度(int *)。

输入对象首先以通常的方式强制转换为 Unicode,然后使用给定的编码将其编码为字符串。

如果 *buffer 非空,则 *buffer_len 必须在输入时设置为 sizeof(buffer)。然后将输出复制到 *buffer

如果 *buffer 为空,则会分配一个所需大小的缓冲区并将输出复制到其中。然后更新 *buffer 以指向分配的内存区域。调用方负责在使用后调用 PyMem_Free() 来释放分配的 *buffer

在这两种情况下,*buffer_len 都将更新为写入的字符数(不包括尾部的 NULL 字节)。确保输出缓冲区以 NULL 结尾。

示例

使用带自动分配的“es#”

static PyObject *
test_parser(PyObject *self,
            PyObject *args)
{
    PyObject *str;
    const char *encoding = "latin-1";
    char *buffer = NULL;
    int buffer_len = 0;

    if (!PyArg_ParseTuple(args, "es#:test_parser",
                          encoding, &buffer, &buffer_len))
        return NULL;
    if (!buffer) {
        PyErr_SetString(PyExc_SystemError,
                        "buffer is NULL");
        return NULL;
    }
    str = PyString_FromStringAndSize(buffer, buffer_len);
    PyMem_Free(buffer);
    return str;
}

使用带自动分配并返回以 NULL 结尾的字符串的“es”

static PyObject *
test_parser(PyObject *self,
            PyObject *args)
{
    PyObject *str;
    const char *encoding = "latin-1";
    char *buffer = NULL;

    if (!PyArg_ParseTuple(args, "es:test_parser",
                          encoding, &buffer))
        return NULL;
    if (!buffer) {
        PyErr_SetString(PyExc_SystemError,
                        "buffer is NULL");
        return NULL;
    }
    str = PyString_FromString(buffer);
    PyMem_Free(buffer);
    return str;
}

使用带预分配缓冲区的“es#”

static PyObject *
test_parser(PyObject *self,
            PyObject *args)
{
    PyObject *str;
    const char *encoding = "latin-1";
    char _buffer[10];
    char *buffer = _buffer;
    int buffer_len = sizeof(_buffer);

    if (!PyArg_ParseTuple(args, "es#:test_parser",
                          encoding, &buffer, &buffer_len))
        return NULL;
    if (!buffer) {
        PyErr_SetString(PyExc_SystemError,
                        "buffer is NULL");
        return NULL;
    }
    str = PyString_FromStringAndSize(buffer, buffer_len);
    return str;
}

文件/流输出

由于 file.write(object) 和大多数其他流写入器使用“s#”或“t#”参数解析标记来查询要写入的数据,因此 Unicode 对象的默认编码字符串版本将写入流(参见缓冲区接口)。

对于使用 Unicode 的文件的显式处理,应使用通过 codecs 模块提供的标准流编解码器。

codecs 模块应该提供一个快捷方式 open(filename,mode,encoding),它还可以确保在需要时 mode 包含字符“b”。

文件/流输入

只有用户知道输入数据使用什么编码,因此不会应用任何特殊技巧。用户必须根据需要显式地将字符串数据转换为 Unicode 对象,或者使用 codecs 模块中定义的文件包装器(参见文件/流输出)。

Unicode 方法和属性

所有 Python 字符串方法,以及

.encode([encoding=<default encoding>][,errors="strict"])
   --> see Unicode Output

.splitlines([include_breaks=0])
   --> breaks the Unicode string into a list of (Unicode) lines;
       returns the lines with line breaks included, if
       include_breaks is true.  See Line Breaks for a
       specification of how line breaking is done.

代码库

我们应该使用 Fredrik Lundh 的 Unicode 对象实现作为基础。它已经实现了大多数所需的字符串方法,并提供了一个良好的代码库,我们可以在此基础上构建。

应删除 Fredrik 的实现中实现的对象共享。

测试用例

测试用例应遵循 Lib/test/test_string.py 中的用例,并包括对编解码器注册表和标准编解码器的其他检查。

参考文献

本提案的历史

[编者注:1.7 之前的修订版本可在标准 Python 发行版的 Misc/unicode.txt 的 CVS 历史记录中找到。所有后续历史记录都可通过此文件的 CVS 修订版本获得。]

1.7

  • 添加了关于“s#”行为变化的说明。

1.6

  • 将 <defencstr> 更改为 <defenc>,因为这是实现中使用的名称。
  • 添加了关于在缓冲区协议实现中使用 <defenc> 的说明。

1.5

  • 添加了关于设置 <default encoding> 的说明。
  • 修复了一些错别字(感谢 Andrew Kuchling)。
  • 将 <defencstr> 更改为 <utf8str>。

1.4

  • 添加了关于混合类型比较和 contains 测试的说明。
  • 更改了在格式字符串中处理 Unicode 对象的方式(如果与 '%s' % u 一起使用,它们现在将导致格式字符串强制转换为 Unicode,从而在返回时生成一个 Unicode 对象)。
  • 添加了到 IANA 字符集名称的链接(感谢 Lars Marius Garshol)。
  • 添加了新的编解码器方法 .readline().readlines().writelines()

1.3

  • 添加了新的“es”和“es#”解析器标记

1.2

  • 删除了关于 codecs.open() 的 POD。

1.1

  • 添加了关于比较和哈希值的说明。
  • 添加了关于大小写映射算法的说明。
  • 更改了流编解码器 .read().write() 方法以匹配标准文件类对象方法(方法不再返回消耗的字节信息)

1.0

  • 更改了 encode 编解码器方法使其与 decode 方法对称(它们现在都返回 (object, data consumed),因此可以互换);
  • 删除了 Codec 类的 __init__ 方法(这些方法是无状态的)并将 errors 参数移到了方法中;
  • 使 Codec 设计更通用,适用于输入和输出对象类型;
  • StreamWriter.flush 更改为 StreamWriter.reset 以避免覆盖流的 .flush() 方法;
  • .breaklines() 重命名为 .splitlines()
  • 将模块 unicodec 重命名为 codecs;
  • 修改了文件 I/O 部分以引用流编解码器。

0.9

  • 更改了 errors 关键字参数定义;
  • 添加了“replace”错误处理;
  • 更改了编解码器 API 以在输入时接受类似缓冲区的对象;
  • 一些小的错别字修复;
  • 添加了空格部分,并包含了具有空格和换行符特征的 Unicode 字符的引用;
  • 添加了说明搜索函数可以预期小写编码名称的说明;
  • 删除了编解码器 API 中的分片和偏移量

0.8

  • 添加了 encodings 包和原始 unicode 转义编码;
  • 取消了建议的制表符;
  • 添加了关于 Unicode 格式字符串的说明;
  • 添加了 .breaklines() 方法

0.7

  • 添加了一整套新的编解码器 API;
  • 添加了不同的编码器查找方案;
  • 修复了一些名称

0.6

  • 将“s#”更改为“t#”;
  • 将 <defencbuf> 更改为 <defencstr>,它保存一个真实的 Python 字符串对象;
  • 将缓冲区接口更改为将请求委托给 <defencstr> 的缓冲区接口;
  • 删除了对 unicodec.codecs 字典的显式引用(模块可以以适合目的的方式实现这一点);
  • 删除了可设置的默认编码;
  • UnicodeError 从 unicodec 移到 exceptions;
  • “s#”不再返回内部数据;
  • 将 UCS-2/UTF-16 检查从 Unicode 构造函数传递到编解码器

0.5

  • sys.bom 移到 unicodec.BOM
  • 添加了关于大小写映射的部分,
  • 专用使用编码和 Unicode 字符属性

0.4

  • 添加了编解码器接口、关于 %-格式化的说明,
  • 更改了一些编码细节,
  • 添加了关于流包装器的注释,
  • 修复了一些讨论要点(最重要的是:内部格式),
  • 阐明了“unicode-escape”编码,添加了编码引用

0.3

  • 添加了引用、关于编解码器模块、内部格式、bf_getcharbuffer 和 RE 引擎的注释;
  • 添加了 Tim Peters 提出的“unicode-escape”编码并相应地修复了 repr(u)

0.2

  • 集成了 Guido 的建议,添加了流编解码器和文件包装

0.1

  • 第一个版本

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

上次修改时间:2024-08-20 10:29:32 GMT