(Python)使用UTF-8编码将字符串写入CSV

3
我有数据库中的数据,想要将其导出为CSV文件。这些数据是用日语编写的,为了进一步使用,我需要将其编码为UTF-8。
以下是我的脚本,用于获取数据并将其写入CSV文件。
import mysql.connector
from mysql.connector import errorcode

import sys
import csv

query = 'SELECT * FROM `images-data`'

try:
    cnx = mysql.connector.connect(user='root', password='1234',
                                 host='127.0.0.1',
                                 database='sotsuken-test-db')
    cur=cnx.cursor()
    cur.execute(query)
    result=cur.fetchall()

    c = csv.writer(open("db-data.csv","w"))
    for row in result:
        c.writerow(row)



except mysql.connector.Error as err:
    if err.errno == errorcode.ER_ACCESS_DENIED_ERROR:
        print("Something is wrong with your user name or password")
    elif err.errno == errorcode.ER_BAD_DB_ERROR:
        print("Database does not exist")
    else:
        print(err)
else:
    cnx.close()

我可以创建CSV文件,并且数据以UTF-8导出,但是我的CSV文件的数据是这样的:
1,b'\xe3\x83\x86\xe3\x82\xb9\xe3\x83\x88\xe3\x81\xa7\xe3\x81\x94\xe3\x81\x8a\xe3\x81\x96\xe3\x81\x84\xe3\x81\xbe\xe3\x81\x99'

在我的研究中,我发现我的数据是以字节对象的形式编写的(也许我在这里错了)。我需要将数据写成UTF-8格式,而不是 b''
我知道我可以使用decode("UTF-8")将其转换为字符串,但我无法将其应用于csv.writerow
你能给我一些建议吗?

为什么不使用csv.DictWriter命令,您可以解码('UTF-8')您的单个单元格值,并使用DictWriter将其写入CSV文件中的特定列。 - Surya Tej
感谢您提出了一个漂亮、简洁、写得很好的问题,遵循了所有新问题/主题/最小化可重现示例的指南。 - Patrick Artner
1
@SuryaTej DictWriter在解决这个问题上比writer更好在哪里?当然,如果你知道你的列是什么,它对于其他原因可能更好,但这并不改变其中一些列是bytes的事实。 - abarnert
顺便提一下,在这里你实际上不需要执行 fetchall。你可以只是迭代 for row in cur:,游标应该会一次给你一行数据,缓冲尽可能多的行数据到内存中,以最高效的方式进行读取,而不管它们的数量。此外,通常不关闭已经打开用于写入的文件是一个坏主意,无论是使用 close() 调用还是 with 语句。 - abarnert
1个回答

2
"csv.writer.writerow"只需要一个任意类型的列表,1对每个元素调用str,并将它们组合成CSV行。如果你所拥有的是bytes对象,那么它会对它们调用str,得到像b'\xe3\x83\x86\xe3\x82\xb9\xe3\x83\x88\xe3\x81\xa7\xe3\x81\x94\xe3\x81\x8a\xe3\x81\x96\xe3\x81\x84\xe3\x81\xbe\xe3\x81\x99'这样的字符串。解决方法是提供字符串。"
最简单的方法是通过解码这些“字节”来完成:

我知道我可以使用 decode("UTF-8") 将其转换为字符串,但我无法将其应用于 csv.writerow

当然可以。我不知道你的列是什么,所以我会举一个自己的例子:
for image_id, image_name in result:
    c.writerow([image_id, image_name.decode('UTF-8')])

这就是全部了。
如果你的代码完全不知道数据库中的列是什么,怎么办?那么,在这种情况下,你必须变得有点聪明。只要我们知道所有的bytes列都是伪装成UTF-8的Unicode文本,你就可以根据类型进行切换:
for row in result:
    textrow = [col.decode('UTF-8') if isinstance(col, bytes) else col for col in row]
    csv.writerow(textrow)

这有点丑陋,但是从一个你不知道列名的数据库中读取*本质上就是一个丑陋的问题。
然而,可能有一个更好的解决方案。
MySQL Connector/Python默认将所有CHARTEXT和类似的列转换为Unicode str值。2 但是BINARYBLOB和类似的列总是作为bytes返回。
如果该列应该表示Unicode文本,请在数据库中将其设置为文本类型而不是二进制类型。这样,在第一次使用此脚本或任何其他工具时,您就不会遇到问题了。
1. 实际上,根据文档的规定,使用除字符串和数字以外的任何列表调用它都是非法的。但实际上,它接受任何类型的输入,只是对于除了字符串和数字以外的其他类型不会做出非常有用的处理...
2. 也就是说,除非您在connect调用中显式地传递use_unicode=False参数或在其他地方进行类似的设置,否则它将默认使用Unicode编码。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接