codecs.open(utf-8) 无法读取纯 ASCII 文件
codecs.open(utf-8) fails to read plain ASCII file
我有一个普通的 ASCII 文件。当我尝试用 codecs.open(..., "utf-8")
打开它时,我无法读取单个字符。 ASCII 是 UTF-8 的一个子集,那么为什么 codecs
不能以 UTF-8 模式打开这样的文件?
# test.py
import codecs
f = codecs.open("test.py", "r", "utf-8")
# ASCII is supposed to be a subset of UTF-8:
# http://www.fileformat.info/info/unicode/utf8.htm
assert len(f.read(1)) == 1 # OK
f.readline()
c = f.read(1)
print len(c)
print "'%s'" % c
assert len(c) == 1 # fails
# max% p test.py
# 63
# '
# import codecs
#
# f = codecs.open("test.py", "r", "utf-8")
#
# # ASC'
# Traceback (most recent call last):
# File "test.py", line 15, in <module>
# assert len(c) == 1 # fails
# AssertionError
# max%
系统:
Linux max 4.4.0-89-generic #112~14.04.1-Ubuntu SMP Tue Aug 1 22:08:32 UTC 2017 x86_64 x86_64 x86_64 GNU/Linux
当然它适用于常规 open
。如果我删除 "utf-8"
选项,它也会起作用。还有 63
是什么意思?这就像第三行的中间。没看懂。
找到您的问题:
当通过编码时,codecs.open
return 是一个 StreamReaderWriter
,它实际上只是一个包装器( 不是 的子类;这是 "composed of" 关系,而不是继承)StreamReader
和 StreamWriter
。问题是:
StreamReaderWriter
提供了一个"normal" read
方法(也就是说,它需要一个 size
参数,仅此而已)
- 它委托给了内部的
StreamReader.read
method,其中size
参数只是提示要读取的字节数,而不是限制; 第二个参数,chars
,是一个严格的限制器,但是StreamReaderWriter
从不传递那个参数(它不接受它)
- 当
size
提示,但未使用 chars
限制时,如果 StreamReader
有缓冲数据,并且它足够大以匹配 size
提示 StreamReader.read
盲目地 returns 缓冲区的内容,而不是基于 size
提示以任何方式限制它(毕竟,只有 chars
强加了 maximum return 尺码)
StreamReader.read
的 API 和 size
/chars
对 API 的含义是这里唯一记录的内容;事实上 codecs.open
returns StreamReaderWriter
不是契约性的,也不是 StreamReaderWriter
包装 StreamReader
的事实,我只是使用了 ipython
's ??
魔术阅读 codecs
模块的源代码以验证此行为。不管有没有文档,这就是它在做的事情(随意阅读StreamReaderWriter
的源代码,都是Python级别的,所以很容易)。
最好的解决方案是切换到 io.open
,这在每个标准情况下都更快更正确(codecs.open
支持不在 bytes
之间转换的怪异编解码器 [ Py2 str
] 和 str
[Py2 unicode
],而是处理 str
到 str
或 bytes
到 bytes
编码,但这是一个非常有限的用例;大多数时候,您在 bytes
和 str
之间进行转换。您需要做的就是导入 io
而不是 codecs
,并将 codecs.open
行更改为:
f = io.open("test.py", encoding="utf-8")
其余代码可以保持不变(并且可能 运行 启动速度更快)。
作为替代方案,您可以显式绕过 StreamReaderWriter
以获取 StreamReader
的 read
方法并直接传递限制参数,例如变化:
c = f.read(1)
至:
# Pass second, character limiting argument after size hint
c = f.reader.read(6, 1) # 6 is sort of arbitrary; should ensure a full char read in one go
我怀疑 Python Bug #8260,它涵盖了在 codecs.open
创建的文件对象上混合 readline
和 read
,正式适用于此,它是 "fixed",但是如果您阅读了评论,则表明修复尚未完成(并且可能无法根据记录的 API 完成); read
和 readline
的任意奇怪组合将能够打破它。
同样,只需使用 io.open
;只要您使用的是 Python 2.6 或更高版本,它就可用,而且更好。
我有一个普通的 ASCII 文件。当我尝试用 codecs.open(..., "utf-8")
打开它时,我无法读取单个字符。 ASCII 是 UTF-8 的一个子集,那么为什么 codecs
不能以 UTF-8 模式打开这样的文件?
# test.py
import codecs
f = codecs.open("test.py", "r", "utf-8")
# ASCII is supposed to be a subset of UTF-8:
# http://www.fileformat.info/info/unicode/utf8.htm
assert len(f.read(1)) == 1 # OK
f.readline()
c = f.read(1)
print len(c)
print "'%s'" % c
assert len(c) == 1 # fails
# max% p test.py
# 63
# '
# import codecs
#
# f = codecs.open("test.py", "r", "utf-8")
#
# # ASC'
# Traceback (most recent call last):
# File "test.py", line 15, in <module>
# assert len(c) == 1 # fails
# AssertionError
# max%
系统:
Linux max 4.4.0-89-generic #112~14.04.1-Ubuntu SMP Tue Aug 1 22:08:32 UTC 2017 x86_64 x86_64 x86_64 GNU/Linux
当然它适用于常规 open
。如果我删除 "utf-8"
选项,它也会起作用。还有 63
是什么意思?这就像第三行的中间。没看懂。
找到您的问题:
当通过编码时,codecs.open
return 是一个 StreamReaderWriter
,它实际上只是一个包装器( 不是 的子类;这是 "composed of" 关系,而不是继承)StreamReader
和 StreamWriter
。问题是:
StreamReaderWriter
提供了一个"normal"read
方法(也就是说,它需要一个size
参数,仅此而已)- 它委托给了内部的
StreamReader.read
method,其中size
参数只是提示要读取的字节数,而不是限制; 第二个参数,chars
,是一个严格的限制器,但是StreamReaderWriter
从不传递那个参数(它不接受它) - 当
size
提示,但未使用chars
限制时,如果StreamReader
有缓冲数据,并且它足够大以匹配size
提示StreamReader.read
盲目地 returns 缓冲区的内容,而不是基于size
提示以任何方式限制它(毕竟,只有chars
强加了 maximum return 尺码)
StreamReader.read
的 API 和 size
/chars
对 API 的含义是这里唯一记录的内容;事实上 codecs.open
returns StreamReaderWriter
不是契约性的,也不是 StreamReaderWriter
包装 StreamReader
的事实,我只是使用了 ipython
's ??
魔术阅读 codecs
模块的源代码以验证此行为。不管有没有文档,这就是它在做的事情(随意阅读StreamReaderWriter
的源代码,都是Python级别的,所以很容易)。
最好的解决方案是切换到 io.open
,这在每个标准情况下都更快更正确(codecs.open
支持不在 bytes
之间转换的怪异编解码器 [ Py2 str
] 和 str
[Py2 unicode
],而是处理 str
到 str
或 bytes
到 bytes
编码,但这是一个非常有限的用例;大多数时候,您在 bytes
和 str
之间进行转换。您需要做的就是导入 io
而不是 codecs
,并将 codecs.open
行更改为:
f = io.open("test.py", encoding="utf-8")
其余代码可以保持不变(并且可能 运行 启动速度更快)。
作为替代方案,您可以显式绕过 StreamReaderWriter
以获取 StreamReader
的 read
方法并直接传递限制参数,例如变化:
c = f.read(1)
至:
# Pass second, character limiting argument after size hint
c = f.reader.read(6, 1) # 6 is sort of arbitrary; should ensure a full char read in one go
我怀疑 Python Bug #8260,它涵盖了在 codecs.open
创建的文件对象上混合 readline
和 read
,正式适用于此,它是 "fixed",但是如果您阅读了评论,则表明修复尚未完成(并且可能无法根据记录的 API 完成); read
和 readline
的任意奇怪组合将能够打破它。
同样,只需使用 io.open
;只要您使用的是 Python 2.6 或更高版本,它就可用,而且更好。