python - Python 编译器如何使用声明的编码预处理源文件?

标签 python encoding python-internals

假设我有一个 cp1251 编码的 Python 3 源文件,其内容如下:

# эюяьъ (some Russian comment)
print('Hehehey')

如果我运行这个文件,我会得到这个:

SyntaxError: Non-UTF-8 code starting with '\xfd' in file ... on line 1 but no encoding declared;有关详细信息,请参见 http://python.org/dev/peps/pep-0263/

这很清楚并且符合预期 - 我知道,一般来说,cp1251 字节序列不能用 UTF-8 解码,UTF-8 是 Python 3 中的默认编码。

但是如果我按如下方式编辑文件:

# coding: utf-8
# эюяьъ (some Russian comment)
print('Hehehey')  

一切都会好起来的。

这很令人困惑。
在第二个示例中,源代码中仍然有相同的 cp1251 字节序列,这在 UTF-8 中无效,我希望编译器应该使用相同的编码 (UTF-8) 来预处理文件并以相同的错误终止.
我读过PEP 263但仍然不明白它没有发生的原因。

那么,为什么我的代码在第二种情况下有效并在第一种情况下终止?


更新

为了检查我的文本编辑器是否足够聪明,可以根据 # coding: utf-8 行更改文件的编码,让我们看看实际的字节数:

(第一个例子)

23 20 fd fe ff fa fc ...

(第二个例子)

23 20 63 6f 64 69 6e 67 3a 20 75 74 66 2d 38 0a
23 20 fd fe ff fa fc ...

这些 f 字节用于 cp1251 中的西里尔字母,它们在 UTF-8 中无效。

此外,如果我以这种方式编辑源代码:

# coding: utf-8
# эюяъь (some Russian comment)
print('Hehehey')
print('эюяъь')

我会遇到错误:

语法错误:(unicode 错误)'utf-8' 编解码器无法解码字节 0xfd ...

所以,不幸的是我的文本编辑器不是那么聪明。
因此,在上面的示例中,源文件没有从 cp1251 转换为 UTF-8

最佳答案

这似乎是如何强制执行默认编码的严格行为的怪癖。在 the tokenizer function, decoding_gets 中,如果它还没有找到明确的编码声明(tok->encoding 仍然是 NULL),它会逐个字符地检查该行是否无效UTF-8 字符并弹出您看到的引用 PEP 263 的 SyntaxError

但是如果已经指定了编码,check_coding_spec就会定义tok->encodingthat default encoding strict test就被完全绕过了;它不会被声明的编码测试所取代。

通常,这会在实际解析代码时引起问题,但看起来注释是以精简的方式处理的:一旦注释字符 # 被识别,the tokenizer just grabs and discards characters until it sees a newline or EOF ,它根本不会尝试对它们做任何事情(这是有道理的;解析注释只是在浪费时间,而这些时间本可以花在实际运行的东西上)。

因此,您观察到的行为是:编码声明禁用了严格的文件范围字符,通过字符检查在没有显式声明编码时应用有效的 UTF-8,并且注释是特殊的,因此它们的内容被忽略,允许注释中的垃圾字节逃避检测。

关于python - Python 编译器如何使用声明的编码预处理源文件?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/46779898/

相关文章:

python - Python 3.2 中字符串的 'or' 运算符出现问题

Python字符串及str()方法编解码

c++ - 如何将 UTF-8 编码的 std::string 转换为 UTF-16 std::string

python - 如何在CPython源代码中找到[::-1](在python中为反向列表)的实现

python - 拼写错误的 __future__ 导入会导致脚本稍后出错,而不是导入位置出错

python - 如何扩展XSD方案以支持SVG?

python - 变量将被多次实例化

python - 在没有赋值的情况下更改函数的定义

regex - 在 perl 中搜索和用普通破折号替换小数破折号的正则表达式?

python - 为什么一个类变量没有在列表理解中定义,而另一个是?