重新编码Unicode流,因为Ascii忽略了错误

时间:2014-10-10 20:07:55

标签: python python-2.7 encoding stream codec

我正在尝试使用包含奇数字符的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()

2 个答案:

答案 0 :(得分:1)

您正在混合编码和解码端。

对于解码,你做得很好。您将其作为二进制数据打开,chardet第一个1K,然后使用检测到的编码以文本模式重新打开。

但是,您正在尝试使用codecs.getreader进一步将已解码的数据解码为ASCII。该函数返回StreamReader从流中解码数据。那不会起作用。您需要数据编码为ASCII。

但不清楚为什么你首先使用codecs流解码器编码器,当你想做的只是编码一个大块文本一次性,所以你可以记录它。为什么不调用encode方法?

log(csv_file.read().encode('ascii', 'ignore'))

如果你想要一些你可以用作懒的可迭代行的东西,你可以构建一些完全通用的东西,但是只做像{{1}这样的事情要简单得多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")

答案 1 :(得分:1)

我对这个派对来说有点晚了,但这是另一种解决方案,使用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()等,我猜您可能想要使用此功能得到回报。如果你只需要迭代流,那么提供的生成器表达式就会更加简洁。