复制 csv 文件时字符串到字节转换错误

Sac*_*chu 2 python csv python-3.x

我在 Python 3.6.4 中编写了以下代码来读取目录中的 .csv 文件“Data.csv”并将内容复制到 NamedTemporaryFile“temp_file”

file_path= "Data.csv"
temp_file=NamedTemporaryFile(delete=False)
with open(file_path,"rb") as csvfile,temp_file:
    fieldnames=["id","Title","Desc","Comments"]
    reader=csv.DictReader(csvfile,fieldnames=fieldnames)
    writer=csv.DictWriter(temp_file,fieldnames=fieldnames)
    for row in reader:
        temp_row={"id":row["id"],"Title":row["Title"],"Desc":row["Desc"],"Comments":row["Comments"],}
        writer.writerow(temp_row)
        print(row)
Run Code Online (Sandbox Code Playgroud)

执行上面的代码在迭代器行中出现错误(对于阅读器中的行):错误说:

_csv.Error: iterator should return strings, not bytes (did you open the file in text mode?)
Run Code Online (Sandbox Code Playgroud)

我寻找解决方案并找到了这个答案

但这并没有解决我的问题,因为当我将“rb”更改为“rt”时,我开始收到另一个错误

writer.writerow(temp_row)
Run Code Online (Sandbox Code Playgroud)

这个错误说:

TypeError: a bytes-like object is required, not 'str'
Run Code Online (Sandbox Code Playgroud)

我认为这个问题只出现在Python 3中,不影响Python 2

我已经尝试过诸如将字符串转换为字节的函数之类的方法encode()以及decode()一些库函数,例如json.dumps().

Mar*_*ers 5

您以二进制模式打开文件:

with open(file_path,"rb") as csvfile,temp_file:
    #                ^^ b is for binary
Run Code Online (Sandbox Code Playgroud)

不要那样做。错误消息告诉您不应该这样做,它特别提醒您以文本模式打开文件:

(did you open the file in text mode?)
Run Code Online (Sandbox Code Playgroud)

b从文件模式中删除。

该类还默认为二进制模式,您需要告诉它以文本NamedTemporaryFile()模式'w+b'打开文件;使用'w+''w'

更正的代码:

temp_file = NamedTemporaryFile(mode='w+', delete=False)
with open(file_path, "r") as csvfile, temp_file:
Run Code Online (Sandbox Code Playgroud)

在Python 2中,该csv模块只能以二进制模式处理数据,部分原因是Python在首次创建该模块时没有强大的Unicode支持,并且因为在Python 2中,某些平台上的文本模式文件以与CSV 标准。

在 Python 3 中,强烈建议newline=''您在打开文件时添加以下内容来关闭行结束翻译:

temp_file = NamedTemporaryFile(mode='w+', newline='', delete=False)
with open(file_path, "r", newline='') as csvfile, temp_file:
Run Code Online (Sandbox Code Playgroud)

我还强烈考虑显式设置编码,而不是依赖系统默认值。

附带说明一下,在复制字典时无需拼写出所有列名称,甚至不必为每一行创建一个新字典,因为DictReader()DictWriter fieldnames列表匹配。您可以将 reader 直接传递给该DictWriter.writerows()方法:

file_path = "Data.csv"
fieldnames = ["id", "Title", "Desc", "Comments"]
temp_file = NamedTemporaryFile(mode='w+', newline='', delete=False)
with open(file_path, "r", newline='') as csvfile, temp_file:
    reader = csv.DictReader(csvfile, fieldnames=fieldnames)
    writer = csv.DictWriter(temp_file, fieldnames=fieldnames)
    writer.writerows(reader)
Run Code Online (Sandbox Code Playgroud)

此时我确实想知道您是否只想使用shutil.copyfileob()并避免解析为字典,然后序列化回 CSV。