【问题标题】:Python 3.8.5 alternative to .replace with csv.reader and UTF-8 mystery encodings用 csv.reader 和 UTF-8 神秘编码替代 .replace 的 Python 3.8.5
【发布时间】:2021-10-02 14:31:35
【问题描述】:

我在 SO 的黑暗中度过了 5 个小时,所以我将这个问题作为最后的手段发布,我真诚地希望有人能在这里指出我正确的方向:

场景:

  • 我有一些来自多种语言的 Google 调查的 .csv 文件(UTF-8 CSV:使用 file -I 命令验证)。输出:

    download.csv: application/csv; charset=utf-8

  • 我有一个“字典”文件,其中包含问题和答案的翻译(一列是 $language,另一列是英语)。

  • Google 的数据中有很多特殊类型的字符(变音符号和法语重音字母等),因为法语、德语、荷兰语

  • 我构建的字典文件可以很好地读取为 UTF-8,包括特殊字符,并准确地创建查找/替换键(使用打印命令验证)

问题在于 Google 文件只能使用 Python 中的 csv.read 函数正确读取(保持正确的字符)。但是,该函数没有 .replace,所以我可以做一个或另一个:

  • 读入源文件,不做任何替换,得到一个完美的副本(不是我需要的)
  • 将 csv 文件/行转换为文件输入/字符串(请记住,UTF-8 仍然是),并得到一个完全颠簸的输出文件,缺少替换,因为数据以某种方式“松散”了 csv 读取和字符串之间的编码?

代码(此处)最接近工作,除了 csv.reader 上没有 .replace 方法:

import csv  

#set source, output
source = 'fr_to_trans.csv'
output = 'fr_translated.csv'
dictionary = 'frtrans.csv'
find = []
replace = []

# build the dictionary itself:
with open(dictionary, encoding='utf-8') as dict_file:
    for line in dict_file:
        #print(line)
        temp_split = []
        temp_split = line.split(',')
        if "!!" in temp_split[0] :
            temp_split[0] = temp_split[0].replace("!!", ",")
        find.append(temp_split[0])
        if "!!" in temp_split[1] :
            temp_split[1] = temp_split[1].replace("!!", ",")
        replace.append(temp_split [1])
        #print(len(find))
        #print(len(replace))

#set loop counters
check_each = len(find)
# Read in the file to parse
with open(source, 'r', encoding='utf-8') as s_file, open(output, 'w', encoding='utf-8') as t_file :
    output_writer = csv.writer(t_file)
    for row in csv.reader(s_file):
        the_row = row
        print(the_row) #THIS RETURNS THE CORRECT, FORMATTED, UTF-8 DATA
        i = 0
        # find and replace everything in the find array with it's value in the replace array
        while i < check_each :  
            print(find[i])
            print(replace[i])
            # THIS LINE DOES NOT WORK:
            the_row = the_row.replace(find[i], replace[i])
            i = i + 1
        output_writer.writerow(the_row)

我不得不假设即使 Google 文件它们是 UTF-8,它们也是一种特殊的“Google 品牌 UTF-8”或类似的废话。使用 csv.reader 正确打开文件,但您对此无能为力,这一事实令人愤怒至极。

只是为了澄清我的尝试:

  • 将文件视为文本,让 Python 整理编码(失败)
  • 将文件视为 UTF-8 文本(失败)
  • 以 UTF-8 格式打开文件,替换字符串,并使用 csv.writer 写出(失败)
  • 将 the_row 转换为字符串,然后替换,然后用 csv.writer 写出(失败)
  • 快速编辑 - 尝试使用字符串的 utf-8-sig - 更好,但输出仍然完全损坏,因为它不是将其读取为 csv,而是字符串

我没试过:

  • “逐个单元格”比较,而不是整行(在这渗透到 SO 时进行处理)
  • 文件的不同编码(我只能获取 UTF-8 CSV,所以需要某种实用程序?)

如果这些是 ASCII 文本,我会在很久以前就完成了,但是整个“UTF-8 不是但是”的事情让我发疯了。有人对此有任何想法吗?

【问题讨论】:

  • 没有“特殊的 Google 品牌 UTF-8”之类的东西,您必须比这更具体。
  • “把文件当成文本,让 Python 整理编码(失败)” 也没有“神奇的文本文件编码检测”这回事。 Python 无法为您解决这个问题。无论是通过 HTTP 标头还是通过字节顺序标记都明确声明了编码,或者您需要在打开文本文件之前知道它。
  • 您可能希望添加文件的相关示例,以 a) 显示文件结构,并且 b) 可重现地演示问题。总体而言,这并不像您的问题中看起来那么难,但是您混淆了一些概念。
  • 我希望可以,但它们包含大量 PII/品牌信息/专有信息,我无法共享源数据文件。我认为最大的问题是,与大多数查找/替换练习不同,我想有效地替换工作表的整个单元格,例如通过匹配法语中的完整短语,然后用英文翻译替换它的每一个出现。当未保留特殊字符的文件编码并且匹配失败/输出因特殊字符而损坏时,就会出现此问题。为什么以 UTF-8 和原生 CSV 格式读取文件是不同的,这是症结所在。
  • 以相同格式发布虚假数据。我们不需要很多行。

标签: python csv utf-8


【解决方案1】:

csv.reader 产生的每一行都是一个列表单元格值,例如

['42', 'spam', 'eggs']

这样一行

# THIS LINE DOES NOT WORK:
the_row = the_row.replace(find[i], replace[i])

不可能工作,因为列表没有替换方法。

可能可行的是遍历行列表并查找/替换每个单元格值(我假设它们都是字符串)

the_row = [cell.replace(find[i], replace[i]) for cell in the row]

但是,如果您只想用一些其他字符替换文件中某些字符的所有实例,那么将文件作为文本文件打开并替换而不调用任何 csv 机制会更简单:

with open(source, 'r', encoding='utf-8') as s_file, open(output, 'w', encoding='utf-8') as t_file :
    text = source.read()
    for old, new in zip(find, replace):
        text = text.replace(old, new)
    t_file.write(text)
 

如果所有文件的查找/替换映射都相同,则可以使用str.translate 来避免 for 循环。

# Make a reusable translation table
trans_table = str.maketrans(dict(zip(find, replace)))

with open(source, 'r', encoding='utf-8') as s_file, open(output, 'w', encoding='utf-8') as t_file :
    text = source.read()
    text = text.translate(trans_table)
    t_file.write(text)

为清楚起见:csvs 是文本文件,仅对其内容进行了格式化,以便其内容可以解释为行和列。如果您想将它们的内容作为纯文本进行操作,可以将它们作为普通文本文件进行编辑:只要您不更改任何用作分隔符或引号的字符,它们在您想要使用时仍然可以用作 csvs他们就是这样。

【讨论】:

  • 我希望你能加入,你在另一篇文章中的回答也让我想到了 utf-sig。我尝试了 cell.replace 并且它有点工作,但是将翻译值的每个字母都放入它自己的列中。 trans_table 方法在查找和替换中的值上失败,因为我还不明白如何使用它(仍在研究),但可能是它。问题是我正在匹配其他语言中的整个短语,这些短语必须匹配完整的短语才能替换完整的短语。它们不是每次都在同一个字段中,或者总是以相同的顺序。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2021-04-04
  • 1970-01-01
  • 2014-01-24
  • 1970-01-01
  • 2018-02-18
  • 2018-01-14
  • 1970-01-01
相关资源
最近更新 更多