【问题标题】:Re-encode Unicode stream as Ascii ignoring errors将 Unicode 流重新编码为 Ascii 忽略错误
【发布时间】:2014-12-06 02:04:50
【问题描述】:

我正在尝试获取一个包含奇数字符的 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()

【问题讨论】:

  • 为什么不直接使用io.open(self.csv_path, 'r', encoding='ascii', errors='ignore')
  • @dano 我的印象是,如果我以 ascii 格式打开一个 UTF-16 文件,每 8 位将被错误地解释为一个字符,而不是每 16 位,整个文件将被误读。我的目标是以正确的编码读取文件,然后即时将其转换为 ascii 流。

标签: python python-2.7 encoding stream codec


【解决方案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() 等,我想您可能想要使用它。如果只需要对流进行迭代,那么提供的生成器表达式 abarnert 会更简洁一些。

【讨论】:

    【解决方案2】:

    您正在混淆编码和解码方面。

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

    但随后您尝试使用codecs.getreader 将已解码的数据进一步解码为ASCII。该函数返回一个StreamReader,它解码来自流的数据。那是行不通的。您需要将该数据编码为 ASCII。

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

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

    如果您想要一些可以用作惰性可迭代行的东西,您可以构建一些完全通用的东西,但是只需执行类似 @ 中的 UTF8Recorder 示例的操作要简单得多987654323@文档:

    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)
    

    【讨论】:

    • 感谢您查看此内容。该日志仅用于调试目的,因此我可以看到我的读者吐出的内容。我希望做与您正在做的事情相同的事情(重新编码为 ascii),但在流上。这可能吗?
    • 我已经按照您的建议进行了操作(将 getreader 转换为 getwriter),现在 ascii_stream.read() 只是返回原始流(EventId,Rate,Attribute1,Attribute2,(。・ω・。)ノ... 即无效的 ASCII)。我错过了其他步骤吗? (就像从作者的输出中开始一个新的读者?)
    • @Alain:当然可以。只是不使用解码阅读器。如果你能准确地展示你想要做什么,我可以告诉你如何去做。但是您可能只想查看csv 文档中的UTF8Recoder 之类的示例,或者我认为Unicode HOWTO 中有一些示例,直到您明白为止。
    • 我的最终目标是通过 csv.reader 运行流 - 它只接受 ascii 流。我已经查看了 UTF8Recoder,但很难弄清楚流在什么时候从原始编码转换为所需的编码(在我的情况下是 Ascii,但在这里似乎是 UTF8)。
    • csv_ascii_stream = (line.encode('ascii', 'ignore') for line in csv_file) 行似乎奏效了!非常感谢!
    猜你喜欢
    • 1970-01-01
    • 2016-07-07
    • 2016-06-19
    • 1970-01-01
    • 1970-01-01
    • 2015-02-03
    • 1970-01-01
    • 1970-01-01
    • 2022-06-15
    相关资源
    最近更新 更多