假设我有一个 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->encoding
,that 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/