【问题标题】:How to ignore Unicode whitespace characters encoded in UTF-8?如何忽略以 UTF-8 编码的 Unicode 空白字符?
【发布时间】:2018-05-25 11:25:38
【问题描述】:

我有一个包含以下信息的 csv 文件:

id  name    age     height  weight
1   x       12      11      124
2   y       13      23      432
3   z       14      43      1435

它存储在名为 Workbook2.csv 的文件中 我使用以下代码:

ipFile = csv.DictReader(open('Workbook2.csv', 'rU'))
dict = {} # Tring to update the rows to this dictionary.
for row in ipFile:
    print row

我得到以下结果:

{'weight': '124', '\xef\xbb\xbfid': '1', 'height ': '11', 'age   ': '12', 'name ': 'x'}
{'weight': '432', '\xef\xbb\xbfid': '2', 'height ': '23', 'age   ': '13', 'name ': 'y'}
{'weight': '1435', '\xef\xbb\xbfid': '3', 'height ': '43', 'age   ': '14', 'name ': 'z'}

我想知道如何将此输出更新为字典。 我还想知道如何忽略使用 UTF-8 编码的 unicode 字符,如果有过滤器可以用来消除它们。

【问题讨论】:

  • 大概这是 Python 2?
  • 看起来像一些 BOM 标头
  • 你的行有一个 UTF-8 BOM 前缀。在每一行。最初的数据是如何产生的?
  • 这是在 Python2 中的,但我也必须在 Python3 中纠正它。
  • @MartijnPieters 是的,这就是数据的产生方式。

标签: python python-2.7 csv utf-8 byte-order-mark


【解决方案1】:

我认为输出明显被误解了。

DictReader 从第一行获取字段名,第一列(在不可见 BOM 之后的那个)只是“id”。这就是为什么 id 字段现在在每条记录中都添加了 BOM。

在 python 2.7 和 3.6 中,我必须使用方言 csv.excel_tab 才能将制表符解释为分隔符。

您的输入数据/csv 文件绝对没问题,因为开头只有一个 BOM(它应该在哪里)。您只需要在阅读之前剥离 BOM。

例如像这样:

from codecs import BOM_UTF8
csv_file = open('test2.csv', 'rU')
csv_file.seek(len(BOM_UTF8))
ipFile = csv.DictReader(csv_file, dialect=csv.excel_tab)

【讨论】:

    【解决方案2】:

    有一个 kwarg skipinitialspace,但我从 C 代码中验证它只查找''。

    两种可能:

    1. 子类 DictReader 以添加一些代码以去除空格
    2. 在将文件中的行传递到 DictReader 之前对其进行按摩。

    (2)的一个例子是:

    import io
    with io.open('Workbook2.csv', 'r', encoding='utf8') as infile:
        ipFile = csv.DictReader((x.replace(u"\uFEFF", u" ") for x in infile))
    ....
    

    【讨论】:

    • 没有必要先将所有内容读入多个列表。使用生成器表达式,例如(x.replace(u"\uFEFF", u"") for x in infile)。并不是说这些行是 Unicode 字符串,所以你的替换会失败。
    • 已编辑以确保我们从 UTF8 编码文件中获取 Unicode 字符串。没有时间验证 CSV DictReader 是否可以处理 Unicode 字符串。
    • 但是csv.DictReader() 无法处理 Unicode 输入
    • 您仍然将整个文件作为一个大列表读入内存,然后生成一个新列表,其中删除了零宽度不间断空格字符,然后才将其传递给阅读器。没有必要浪费所有这些内存。
    • 您仍在使用infile.readlines() 阅读整个文件。而且您仍然期望 Python 2 csv 模块能够处理 Unicode 字符串。只有在数据是 ASCII 干净的情况下才有效(偶然)。
    【解决方案3】:

    您的输入数据在每一行上都包含UTF-8 BOM sequences。无论生成什么这个文件,似乎都是使用utf-8-sig 编解码器或非 Python 等价物一次添加一行数据。 BOM(如果使用的话)应该是文件中的第一个字符,并且不要在其他任何地方使用。您的数据已损坏,如果您可以从源头修复此问题,请执行此操作。

    但是,当您阅读时,有一种方法可以解决此问题。 csv 模块读取的“文件”可以是在您迭代时产生行的任何内容。首先使用生成器过滤文件行:

    from codecs import BOM_UTF8
    
    def bom_filter(lines):
        for line in lines:
            if line.startswith(BOM_UTF8):
                line = line[len(BOM_UTF8):]
            yield line
    

    然后在将文件传递给DictReader() 对象之前通过过滤器:

    with open('Workbook2.csv', 'rU') as inputfile:
        ipFile = csv.DictReader(bom_filter(inputfile))
    

    演示:

    >>> from io import BytesIO
    >>> import csv
    >>> from codecs import BOM_UTF8
    >>> def bom_filter(lines):
    ...     for line in lines:
    ...         if line.startswith(BOM_UTF8):
    ...             line = line[len(BOM_UTF8):]
    ...         yield line
    ...
    >>> demofile = BytesIO('''\
    ... \xef\xbb\xbfid,name,age,height,weight
    ... \xef\xbb\xbf1,x,12,11,124
    ... \xef\xbb\xbf2,y,13,23,432
    ... \xef\xbb\xbf3,z,14,43,1435
    ... ''')
    >>> ipFile = csv.DictReader(bom_filter(demofile))
    >>> for row in ipFile:
    ...     print row
    ...
    {'age': '12', 'height': '11', 'id': '1', 'weight': '124', 'name': 'x'}
    {'age': '13', 'height': '23', 'id': '2', 'weight': '432', 'name': 'y'}
    {'age': '14', 'height': '43', 'id': '3', 'weight': '1435', 'name': 'z'}
    

    在 Python 3 中,csv 模块采用 Unicode 字符串输入(与字节字符串相反,因此现在您需要查找解码结果,即 U+FEFF 零宽度空间代码点。要使代码在任一Python 版本,您必须在行首换掉您正在测试的内容:

    import sys
    to_filter = u'\ufeff'
    if sys.version_info < (3,):
        to_filter = to_filter.encode('utf8')
    
    def bom_filter(lines):
        for line in lines:
            if line.startswith(to_filter):
                line = line[len(to_filter):]
            yield line
    

    【讨论】:

    • 感谢您的回答。它解决了 BOM 问题。但我仍然在其他数据集中得到 \xc2\xc0 。你能指导我忽略这些不可见的空格等吗?
    • @TheTank:这是一个不完整的 UTF-8 字节序列,所以我无法真正评论该序列的含义以及如何处理它。
    • @TheTank:您有 UTF-8 数据,因此该流中很可能有非 ASCII 数据不是空白。但我无法从不完整的字节序列中看出这一点。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2013-01-16
    • 2014-06-09
    • 2018-01-14
    • 1970-01-01
    • 2019-09-17
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多