如何在 100 个文本文件(每个文件有 50k 行)上的 20k 术语的“搜索和替换”中执行最少的磁盘 I/O

Ins*_*ico 3 perl sed

我想做一个 \xe2\x80\x98 搜索并用非结构化文本替换大约 100 个数据文件上的 \xe2\x80\x99 。每个文件大小约为 50 MB,行数为 50k。大约有20k个术语需要搜索和替换,它们存储在另一个文件中;\'terms_list.csv\',一个包含三列 COL1、COL2 和 COL3 的 CSV 格式文件。我需要在 100 个数据文件中的每个数据文件的 \'terms_list.csv\' 的 COL1 和 COL2 中搜索单词,如果找到其中一个单词,则替换为 COL3 中的相应单词。

\n\n

凭借我对 shell 脚本编写的基本知识,我使用 AWK/SED 循环编写了以下 shell 脚本。它从 20k 行 \xe2\x80\x98terms_list.csv\xe2\x80\x99 中逐行读取,并在 100 个文件中的每个文件中搜索 COL1 和 COL2,如果找到,将替换为 COL3。

\n\n
for DATA_FILE in $(ls text_data_file_*.csv) #Data files (100 files) with 50k lines; contain terms in COL1 and COL2 of terms_list.csv\ndo\n   while read -r line;  \n       do\n           x=$(echo $line | awk -F\',\' \'{print $1}\'); \\\n           y=$(echo $line | awk -F\',\' \'{print $2}\'); \\\n           z=$(echo $line | awk -F\',\' \'{print $3}\'); \\\n           echo "File: " $DATA_FILE " x: "$x "|" "y: "$y "|" "z: "$z ; \\\n           sed -i "s/$x/$z/;s/$y/$z/g" $DATA_FILE\n       done < terms_list.csv #20k lines in CSV format; each with search terms COL1,COL2, and replace term COL3\ndone\n
Run Code Online (Sandbox Code Playgroud)\n\n

我确信有比上面更好/高效的代码来完成任务,因为这需要大量的磁盘读/写。有什么改进建议吗?如果有更好的工具(perl/python)来完成这项任务,你能给我一些建议/指导吗?

\n\n

以下是这两个文件的示例数据:

\n\n
    \n
  1. \xe2\x80\x98text_data_file_0001.csv\xe2\x80\x99:100 个数据文件之一,\xe2\x80\x98text_data_file_0001.csv\xe2\x80\x99 包含非结构化数据,如下所示,其中包含 \xe2\x80\x98TermFull\文本中的 xe2\x80\x99 和 \xe2\x80\x98TermAbbreviated\xe2\x80\x99 。[每个文件大小约50MB,50k行]

    \n\n
    ID000001,Mangifera indica, commonly known as mango, is a species of flowering plant in the sumac and poison ivy family Anacardiaceae. M. indica is a popular fruit in India. \nID000002,Oryza sativa, commonly known as Asian rice, is the plant species most commonly referred to in English as rice. O. sativa contains two major subspecies: the sticky, short-grained japonica or sinica variety, and the nonsticky, long-grained indica rice variety.\n
    Run Code Online (Sandbox Code Playgroud)
  2. \n
  3. \'terms_list.csv\' 文件:搜索词 \'TermFull\' 和 \'TermAbbreviated\' 以及替换词 \'TermJoined\' 存储在 \xe2\x80\x98terms_list.csv\xe2\x80\x99 中包含20k行如下

    \n\n
    TermFull,TermAbbreviated,TermJoined\nMangifera indica,M. indica,Mangiferaindica\nOryza sativa,O. sativa,Oryzasativa\n
    Run Code Online (Sandbox Code Playgroud)
  4. \n
  5. 所需的输出文件 \xe2\x80\x98text_data_file0001.csv\xe2\x80\x99 如下所示,其中 \xe2\x80\x98TermFull\xe2\x80\x99 和 \xe2\x80\x98TermAbbreviated\xe2\x80\x99 替换为 \xe2 \x80\x98TermJoined\xe2\x80\x99

    \n\n
    ID000001,Mangiferaindica, commonly known as mango, is a species of flowering plant in the sumac and poison ivy family Anacardiaceae. Mangiferaindica is a popular fruit in India. \nID000002,Oryzasativa, commonly known as Asian rice, is the plant species #most commonly referred to in English as rice. Oryzasativa contains two major subspecies: the sticky, short-grained japonica or sinica variety, and the nonsticky, long-grained indica rice variety.\n
    Run Code Online (Sandbox Code Playgroud)
  6. \n
\n

Ed *_*ton 5

这是完整的方法(即不需要周围的 shell 循环或其他任何东西),使用 GNU awk 进行“就地”编辑:

awk -i inplace -F, '
NR==FNR { if (NR>1) { map[$1]=$3; map[$2]=$3 } print; next }
{
    for (term in map) {
        gsub(term,map[term])
    }
    print
}
' terms_list.csv text_data_file_*.csv
Run Code Online (Sandbox Code Playgroud)

按摩以适应。例如,如果看起来你的 terms_list 文件可能包含 RE 元字符,那么你应该考虑是否要在正则表达式中使用它们,就像你在 sed 中所做的那样,所以我们在上面使用 gsub() 所做的,或者使用像使用 index() 和 substr() 而不是 gsub() 的字符串操作,以及如何处理部分匹配和/或第一个替换创建一个先前不存在的术语,然后与第二个替换匹配的情况更换等。

我怀疑这样的东西(未经测试)将足够灵活和强大,足以满足您的需求(它肯定比您正在运行的 sed 脚本更强大,并且比您的 shell 循环 + sed 方法快几个数量级):

awk -i inplace -F, '
NR==FNR {
    orig = $0

    if (NR > 1) {
        gsub(/[^^]/,"[&]",$1)
        gsub(/\^/,"\\^",$1)

        gsub(/[^^]/,"[&]",$2)
        gsub(/\^/,"\\^",$2)

        gsub(/&/,"\\&",$3)

        map["\\<"$1"\\>"] = $3
        map["\\<"$2"\\>"] = $3
    }

    print orig
    next
}
{
    for (term in map) {
        gsub(term,map[term])
    }
    print
}
' terms_list.csv text_data_file_*.csv
Run Code Online (Sandbox Code Playgroud)

gsub 会转义任何元字符,因此原始文本和替换文本中的每个字符都会按字面意思处理,但随后我们会在每个原始术语的整个周围添加单词边界以避免部分匹配。

  • 感谢埃德·莫顿的专家指导。我试图理解代码并在我的数据上运行它。正如我在上面对 Charles 所说的,我需要提高我的基本脚本编写技能。 (2认同)