带有编解码器标头的 ASCII 安全文件的编码问题,具体取决于行数
Posted
技术标签:
【中文标题】带有编解码器标头的 ASCII 安全文件的编码问题,具体取决于行数【英文标题】:Encoding issue with ASCII-safe file with codec header, depending on line count 【发布时间】:2016-12-26 05:22:09 【问题描述】:这是 Windows 上 Python 3.5.2 的神奇错误,它杀死了我的一天。以下文件在此系统上失败:
C:\Python35\python.exe encoding-problem-cp1252.py File "encoding-problem-cp1252.py", line 2 SyntaxError: encoding problem: cp1252
几乎不包含任何内容 - 除了coding
标头之外,还有一堆空行,但是删除任何行,即使是空行,它也会再次起作用。我认为这是一个本地问题,所以我设置了 job on AppVeyor 以显示相同的行为。
Python 发生了什么?
下面的文件有一个binary accurate version:
#!/usr/bin/env python
# -*- coding: cp1252 -*-
"""
There is nothing in this file, except that it is more
than 50 lines long. Running it with Python 3.5.2 on
Windows gives the following error:
>python encoding-problem-cp1252.py
File "encoding-problem-cp1252.py", line 2
SyntaxError: encoding problem: cp1252
>python
Python 3.5.2 (v3.5.2:4def2a2901a5, Jun 25 2016, 22:01:18) [MSC v.1900 32 bit (Intel)] on win32
Type "help", "copyright", "credits" or "license" for more information.
If you remove any lines from this file, it will
execute successfully.
"""
def restore(dump):
"""
"""
return
def main():
print('ok')
if __name__ == '__main__':
main()
【问题讨论】:
不能在windows上运行,这很难调试;该文件在 Mac OS X 和 Linux 上运行良好。听起来你发现了一个错误,请在bugs.python.org 提交一个 查看relevant source code 我怀疑那里的 CRLF 处理。如果您将文件从 Unix 行尾(现在这样)转换为 Windows 行尾,会发生什么?更长的文件肯定会解决缓冲问题。 @MartijnPieters,当文件转换为 CRLF 时,错误消失了,但我不会将所有源代码转换为 CRLF,因为 Windows 是这些脚本的辅助平台。 感谢您确认我的预感是正确的。 报告于bugs.python.org/issue27797 【参考方案1】:这看起来像是由issue #20731 引起的回归。看起来位置计算假设总会有 CRLF 行结尾,而您的文件只有 LF 字符,导致不正确的偏移量being calculated here:
fd = fileno(tok->fp);
/* Due to buffering the file offset for fd can be different from the file
* position of tok->fp. If tok->fp was opened in text mode on Windows,
* its file position counts CRLF as one char and can't be directly mapped
* to the file offset for fd. Instead we step back one byte and read to
* the end of line.*/
pos = ftell(tok->fp);
if (pos == -1 ||
lseek(fd, (off_t)(pos > 0 ? pos - 1 : pos), SEEK_SET) == (off_t)-1)
PyErr_SetFromErrnoWithFilename(PyExc_OSError, NULL);
goto cleanup;
当您将文件转换为使用 Windows (CRLF) 行结尾时,问题就消失了,但我可以理解,对于跨平台脚本,这不是一个实际的解决方案。
我已提交issue #27797;这应该在 Python 本身中修复。
【讨论】:
问题是ftell(tok->fp)
返回-1。触发 bug 所需的换行数取决于 shebang 的长度和编码规范。要计算文件位置ftell
,需要从实际 OS 文件位置中减去缓冲区中未读字节数。问题是缓冲区中的行尾已经被转换为 LF,而 OS 文件位置基于未翻译的行尾。所以ftell
假设文件使用 CRLF 行结尾,因此每个未读 LF 减去 2 个字节。在正确的条件下,它返回 -1,这看起来像一个错误。
其他人? ;-) 顺便说一句,我只查看了使ftell
返回-1 的这些特定情况。它还可以返回小于 -1 的位置,在这种情况下,lseek
调用会失败,因为它是 SEEK_SET
。
@eryksun:哎呀!感谢您的分析!以上是关于带有编解码器标头的 ASCII 安全文件的编码问题,具体取决于行数的主要内容,如果未能解决你的问题,请参考以下文章
UnicodeEncodeError:“ascii”编解码器无法编码字符
Python,Docker - “ascii”编解码器无法编码字符
google gsutil:ascii 编解码器无法编码字符