用Python在CSV文件中查找和替换

0 投票
3 回答
2297 浏览
提问于 2025-04-16 05:29

这段内容和之前的问题有关,我正在尝试对多个大型CSV文件进行替换操作。

这些文件的列顺序(和内容)会有所不同,但每个文件中大约有10列是我需要的,并且我可以通过列标题名称来识别这些列。我还为每个需要的列准备了1到2个字典。所以对于我想要的列,我只想使用正确的字典,并且希望按顺序实现它们。

这是我尝试解决这个问题的一个例子:

# -*- coding: utf-8 -*-
import re

# imaginary csv file. pretend that we do not know the column order.
Header = [u'col1', u'col2']
Line1 = [u'A',u'X']
Line2 = [u'B',u'Y']
fileLines = [Line1,Line2]

# dicts to translate lines
D1a = {u'A':u'a'}
D1b = {u'B':u'b'}
D2 = {u'X':u'x',u'Y':u'y'}

# dict to correspond header names with the correct dictionary.
# i would like the dictionaries to be read sequentially in col1.
refD = {u'col1':[D1a,D1b],u'col2':[D2]}

# clunky replace function
def freplace(str, dict):
    rc = re.compile('|'.join(re.escape(k) for k in dict))
    def trans(m):
        return dict[m.group(0)]
    return rc.sub(trans, str)

# get correspondence between dictionary and column
C = []
for i in range(len(Header)):
    if Header[i] in refD:
        C.append([refD[Header[i]],i])

# loop through lines and make replacements
for line in fileLines:
    for i in range(len(line)):
        for j in range(len(C)):
            if C[j][1] == i:
                for dict in C[j][0]:
                    line[i] = freplace(line[i], dict)

我的问题是,这段代码运行得很慢,我不知道怎么加快速度。我还是个初学者,我猜是我的freplace函数让事情变得缓慢,因为它需要为每一行的每一列进行编译。我想把这一行rc = re.compile('|'.join(re.escape(k) for k in dict))从那个函数中提取出来,但我不知道怎么做才能保留我代码的其他部分的功能。

3 个回答

0

如果是这样的话,假设这10列的名字每次都是一样的,只是顺序不同(我不确定你是不是在做这个,但我就这样说吧),你可以先准备一个数组来存放列名,再准备一个数组来存放每一列的数据(每行应该有10个数据)。接下来,你可以通过一个选择结构来调整正则表达式的使用,比较一下你的列名数组的元素位置,然后在选择结构里面,按照相同的位置引用数据数组。因为列名会帮助你找到正确的顺序,所以你应该可以重复使用同样的10个正则表达式,而不需要每次都重新编写新的“命令”。

希望这样说你能明白。抱歉我不知道具体的语法来帮你,但我希望我的想法是你需要的。

也就是说:

在开始循环之前,先初始化所有的正则表达式。

然后在你读取一行数据(在读取列名那一行之后)的时候,

选择数组[n]

如果是“column1”

就用正则表达式处理(data[0]);

如果是“column2”

就用正则表达式处理(data[1]);

……

最后结束选择结构。

这样就能为正确的列调用正确的正则表达式了。

3

有很多方法可以让这个过程更快:

首先,使用 csv 模块。这个模块提供了高效且没有错误的方法来读取和写入 CSV 文件。特别是 DictReader 对象,它会把从文件中读取的每一行都呈现为一个字典,字典的键是列名。

其次,编译你的正则表达式(regex)只需要做一次,而不是每次使用时都编译。把编译好的正则表达式保存在一个字典里,字典的键是你要应用它们的列名。

第三,考虑一下,如果你对一个长字符串应用一百个正则表达式,你就得从头到尾扫描这个字符串一百次。这可能不是解决问题的最佳方法;你可能更应该花点时间寻找一种方法,让你只需从头到尾读取一次字符串。

1

你不需要用到 re 模块:

# -*- coding: utf-8 -*-

# imaginary csv file. pretend that we do not know the column order.
Header = [u'col1', u'col2']
Line1 = [u'A',u'X']
Line2 = [u'B',u'Y']
fileLines = [Line1,Line2]

# dicts to translate lines
D1a = {u'A':u'a'}
D1b = {u'B':u'b'}
D2 = {u'X':u'x',u'Y':u'y'}

# dict to correspond header names with the correct dictionary
refD = {u'col1':[D1a,D1b],u'col2':[D2]}

# now let's have some fun...

for line in fileLines:
    for i, (param, word) in enumerate(zip(Header, line)):
        for minitranslator in refD[param]:
            if word in minitranslator:
                line[i] = minitranslator[word]

返回结果:

[[u'a', u'x'], [u'b', u'y']]

撰写回答