csv.reader 和 csv.DictReader 将字符串列表(行列表)作为输入,而不仅仅是文件对象。
因此,以二进制模式 (mode="rb") 打开文件,找出每一行的编码,使用该编码解码该行并将其附加到一个列表中,然后在该列表上调用 csv.reader。
一个简单的启发式方法是尝试将每一行读取为 UTF-8,如果您得到 UnicodeDecodeError,请尝试将其解码为其他编码。如果您无法将其解码为 UTF-8,我们可以使用chardet library(与pip install chardet 一起安装)来猜测每行的编码,而不是硬编码要依赖的编码,从而使其更通用:
import codec
my_csv = "some/path/to/your_file.csv"
lines = []
with open(my_csv, "rb") as f:
for line in f:
detected_encoding = chardet.detect(line)["encoding"]
try:
line = line.decode("utf-8")
except UnicodeDecodeError as e:
line = line.decode(detected_encoding)
lines.append(line)
reader = csv.DictReader(lines)
for row in reader:
do_stuff(row)
如果您确实只想硬编码后备编码并且不想使用chardet(有一个很好的理由不这样做,它并不总是准确的),您可以替换变量detected_encoding 与 "Windows-1251" 或 whatever encoding 在上面的代码中。
这当然不是完美的,因为仅仅因为一行使用某种编码成功解码并不意味着它实际上正在使用该编码。如果您不必多次执行此操作,最好执行打印每一行及其检测到的编码之类的操作,并尝试找出一种编码从哪里开始,而另一种则手动结束。最终,在这里追求的正确策略可能是尝试逆转导致输入损坏的步骤(文件的连接),然后正确地重新执行(通过在连接之前将它们标准化为相同的编码)。
就我而言,我计算了检测到的编码行数
import chardet
from collections import Counter
my_csv_file = "some_file.csv"
with open(my_csv_file, "rb") as f:
encodings = Counter(chardet.detect(line)["encoding"] for line in f)
print(encodings)
并意识到我的整个文件实际上是用其他第三种编码编码的。在整个文件上运行chardet 检测到错误的编码,但在每一行上运行它检测到一堆编码,第二个最常见的编码(在ascii 之后)是我需要用来读取整个文件的正确编码。所以最终我需要的只是
with open(my_csv, encoding="latin_1") as f:
reader = csv.DictReader(f)
for row in reader:
do_stuff(row)
您可以尝试使用Compact Encoding Detection 库而不是chardet。这是谷歌浏览器使用的,所以也许它会更好,但它是用 C++ 而不是 Python 编写的。