我试图获取一个包含奇数字符的Unicode文件流,并用流读取器将其包装,流读取器将把它转换为Ascii,忽略或替换所有无法编码的字符。
我的流看起来像:
"EventId","Rate","Attribute1","Attribute2","(。・ω・。)ノ"
...
我试图在飞行中改变流,看起来是这样的:
import chardet, io, codecs
with open(self.csv_path, 'rb') as rawdata:
detected = chardet.detect(rawdata.read(1000))
detectedEncoding = detected['encoding']
with io.open(self.csv_path, 'r', encoding=detectedEncoding) as csv_file:
csv_ascii_stream = codecs.getreader('ascii')(csv_file, errors='ignore')
log( csv_ascii_stream.read() )
log
行上的结果是:UnicodeEncodeError: 'ascii' codec can't encode characters in position 36-40: ordinal not in range(128)
,尽管我使用errors='ignore'
显式构建了StreamReader
我希望得到的流(当阅读时)是这样的:
"EventId","Rate","Attribute1","Attribute2","(?????)?"
...
或者"EventId","Rate","Attribute1","Attribute2","()"
(使用'ignore'
而不是'replace'
)
为什么会发生异常?
我已经看到了很多解码字符串的问题/解决方案,但我的挑战是在读取流时(使用.next()
)更改流,因为文件可能太大,无法使用.read()
一次加载到内存中
您混淆了编码和解码端。
对于解码,你做得很好。您将其作为二进制数据打开,chardet
是第一个1K,然后使用检测到的编码以文本模式重新打开。
但是,您试图通过使用codecs.getreader
将已经解码的数据进一步解码为ASCII。该函数返回一个StreamReader
,对流中的数据进行解码。这行不通。您需要将数据编码为ASCII。
但目前还不清楚为什么首先要使用codecs
流解码器或编码器,而你只想一次性编码一段文本,这样你就可以记录它。为什么不直接调用encode
方法呢?
log(csv_file.read().encode('ascii', 'ignore'))
如果你想要一些可以用作行的惰性迭代的东西,可以构建一些完全通用的东西,但只做csv
文档中的UTF8Recorder
示例会简单得多:
class AsciiRecoder:
def __init__(self, f, encoding):
self.reader = codecs.getreader(encoding)(f)
def __iter__(self):
return self
def next(self):
return self.reader.next().encode("ascii", "ignore")
或者,更简单地说:
with io.open(self.csv_path, 'r', encoding=detectedEncoding) as csv_file:
csv_ascii_stream = (line.encode('ascii', 'ignore') for line in csv_file)
我有点迟到了,但这里有一个替代解决方案,使用codecs.StreamRecoder
:
from codecs import getencoder, getdecoder, getreader, getwriter, StreamRecoder
with io.open(self.csv_path, 'rb') as f:
csv_ascii_stream = StreamRecoder(f,
getencoder('ascii'),
getdecoder(detectedEncoding),
getreader(detectedEncoding),
getwriter('ascii'),
errors='ignore')
print(csv_ascii_stream.read())
如果您需要灵活性,以便能够在返回的流上调用read()
/readlines()
/seek()
/tell()
等,我想您可能需要使用此功能。如果您只需要对流进行迭代,那么提供的生成器表达式abarnert会更简洁一些。