如果值为字母数字,则 csv.DictWriter 返回空白字符串

分享于2023年03月08日 csv python 问答
【问题标题】:csv.DictWriter returning blank strings if value is alphanumeric如果值为字母数字,则 csv.DictWriter 返回空白字符串
【发布时间】:2023-03-07 20:40:02
【问题描述】:

这是我的代码:

import csv
import os

for root, subFolders, files in os.walk('/path/to/folder/'):
    if 'routes.csv' in files:
        with open(os.path.join(root, "R.csv"), "r") as inf, \
                open(os.path.join(root, "B.csv"), "a") as output:
            reader = csv.DictReader(inf, quotechar='"')
            headers = ["R_id"]
            writer_B_routes = csv.DictWriter(output, headers, 
                                             extrasaction='ignore')
            writer_B_routes.writeheader()
            for row in reader:
                if int(row["R_type"]) == 3:
                    writer_B_routes.writerow(row)

当我在我的 CSV 文件夹上运行它时,如果 R_id 的值只是数字,那么它输出到 B.csv 很好(即 1234 )。但是,如果 R_id 的值是字母数字(即 A123 ),那么我在 B.csv 中得到的输出是 ""

我尝试将 int(row["R_type"]) == 3 更改为 str() ,认为方言的构建方式存在一些问题,但没有奏效。我不确定这些数据在哪里只传递了一个整数。

更新: 链接到示例数据+脚本: Here

更新 2: 我已经用其他几个样本进行了测试 - 案例似乎是独一无二的,但我无法确定原因。我有另一组包含 R_id 005M1 的样本数据,它运行正常。给我这个问题的数据有 R_id E2 和类似的。但现在我知道它不适用于所有字母数字 ID。

  • 你试过'if "3" in str(row["R_type")' 吗?
  • 这些字母数字 R_id 值是否在 CSV 文件中(更重要的是,在 row 字典中)?如果在 DictWriter 中使用 extrasaction='raise' 而不是 extrasaction='ignore' ,会发生什么?
  • @Jodgod 刚刚试过,同样的问题。 writer_B_routes.writerow(row) is supposed to write the value of R_id` - 如果它们是字母数字,它似乎会拒绝这些。我还尝试将 quotechar='"' 添加到 DictWriter ,但没有运气。
  • 我会通过调试器运行它以查看 if 上发生了什么以及 row 的值是什么(或仅使用打印)。
  • 显然您在 routes.csv 的开头有一个特殊字符,它搞砸了列名。当我阅读您的文件时,第一列名称是 '\xef\xbb\xbfroute_id' 而不是 'route_id'。检查 this post

【解决方案1】:

请注意,以下内容指的是您在 RemoveBAIO.py 中的代码 不是 您的问题中显示的代码。

问题在于您尝试读取的 routes.csv 文件是一个Unicode 文本文件,开头为UTF-8 Byte-Order-Mark (或BOM),而 csv 正在获取该文件模块——它不能处理 Python 2 中的 Unicode 输入,在 @​​987654322@ 中注明——作为第一个字段的名称,所以它不是 "route_id" ,而是 "\xef\xbb\xbfroute_id"

这是您的代码稍作修改的版本,显示了处理 可能 具有初始 BOM 的文件的正确方法。它通过将 codecs.open() 'utf-8-sig' encoding 结合使用来实现这一点。此编码在 codecs module 文档的 Encodings and Unicode 部分中进行了描述。解码时,这将跳过任何存在的 BOM,而在编码时,它将首先写入 3 字节 BOM 序列。在下面的代码中,它故意仅用于读取输入文件(我解释了为什么进一步向下)。效果是输入文件中的第一个字段名称不会弄乱。

还请注意,我在 'route_type' check 中删除了对 int 的转换,因此如果遇到包含非数字字符的异常,它不会引发 ValueError 异常。

bus_route_ids.csv 没有 有一个领先的 BOM。添加一个会很复杂,因为可能会将数据附加到其中,因此添加 BOM 必须以文件是否已经存在为条件。 FWIW,我还注意到 routes.csv 不是正确的 UTF-8,因为它在最后一行嵌入了一个 \xa0 字符,其序数值大于 128。

import codecs
import csv
import os

path_to_folder = '/insert/path/'

with open('hasfares.txt', 'w') as hf:
    for root, subFolders, files in os.walk(path_to_folder):
        if 'fare_rules.csv' in files:
            hf.write('%s\n' % root)

        if 'routes.csv' in files:
            routes_path = os.path.join(root, 'routes.csv')
            bus_route_ids_path = os.path.join(root, 'bus_route_ids.csv')
            appending_to_existing_file = os.path.exists(bus_route_ids_path)
            with codecs.open(routes_path, 'r', 'utf-8-sig') as inf, \
                    open(os.path.join(root, "bus_route_ids.csv"), "a") as output:
                reader = csv.DictReader(inf, quotechar='"')
                headers = ['route_id']
                writer_bus_routes = csv.DictWriter(output, headers,
                                                   extrasaction='ignore')
                if not appending_to_existing_file:
                    writer_bus_routes.writeheader()
                for row in reader:
                    if row['route_type'] == '3':
                        writer_bus_routes.writerow(row)

生成的 bus_route_ids.csv 文件(假设它不存在):

route_id
E1
E2
N
N1
N2
N3
170
S1
S2
S3
S4
W1
W2
W3
W4
C

【讨论】:

  • @T.Mount:这不能回答你的问题吗?
  • 已继续使用上述 Paulo 的 cmets - 但您的解决方案更容易应用于我的数据集。再次感谢!
  • @T.Mount:我更新了我的答案,以显示处理 BOM 的正确方式,就像不是黑客一样——我最近在 SO 上了解到自己。新年快乐。