使用 sed 命令查找和替换单词不起作用

Find and replace words using sed command not working

我有一个以制表符分隔的文本文件,第一列包含要查找的词,第二列包含要替换找到的词的词。此文本文件包含英语和阿拉伯语对。一旦找到并替换了单词,就不应再更改它。

例如:

adam    a +dam
a   b
ال  ال+

所以对于给定的文本文件:

adam played with a ball ال

我预计:

a +dam played with b ball ال+

但是,我得到:

b +dbm plbyed with b bbll ال+

我正在使用以下 sed 命令查找和替换:

sed -e 's/^/s%/' -e 's/\t/%/' -e 's/$/%g/' tab_sep_file.txt | sed -f - original_file.txt >replaced.txt

我该如何解决这个问题

您的方法的基本问题是您不想将先前替换中的匹配文本替换为后面的替换 - 您不想更改 aa +damb 中。这使得 sed 成为一个非常糟糕的选择 - 您可以创建一个正则表达式来匹配您想要替换的所有内容,但选择使用哪个替换是一个问题。

一种使用 GNU 的方式 awk:

gawk -F'\t' '
     FNR == NR { subs[] = ; next } # populate the array of substitutions
     ENDFILE {
             if (FILENAME == ARGV[1]) {
                # Build a regular expression of things to substitute
                subre = "\<("
                first=0
                for (s in subs)
                    subre = sprintf("%s%s%s", subre, first++ ? "|" : "", s)
                subre = sprintf("%s)\>", subre)
             }
     }
     {
        # Do the substitution
        nwords = patsplit([=10=], words, subre, between)
        printf "%s", between[0]
        for (n = 1; n <= nwords; n++)
            printf "%s%s", subs[words[n]], between[n]
        printf "\n"
     }
' tab_sep_file.txt original_file.txt

输出

a +dam played with b ball

首先,它读取 TSV 文件并构建一个包含要替换的单词和要替换为 (subs) 的文本的数组。然后在读取该文件后,它会构建一个正则表达式来匹配所有可能要找到的单词 - 在本例中为 \<(a|adam)\>\<\> 分别只匹配单词的开头和结尾,所以 aball不匹配。

然后对于包含您要处理的文本的第二个文件,它使用 patsplit() 将每一行拆分为匹配部分 (words) 和匹配之间的位 (between),并遍历数组的长度,打印出每个匹配项的替换文本。这样就可以避免重新匹配已经被替换的文本。


以及使用类似方法的 perl 版本(利用 perls/// 替换中评估替换文本的能力):

perl -e '
     use strict;
     use warnings;
     # Set file/standard stream char encodings from locale
     use open ":locale"; 
     # Or for explicit UTF-8 text
     # use open ":encoding(UTF-8)", ":std";
     my %subs;
     open my $words, "<", shift or die $!;
     while (<$words>) {
        chomp;
        my ($word, $rep) = split "\t" ,$_, 2;
        $subs{$word} = $rep;
     }
     my $subre = "\b(?:" . join("|", map { quotemeta } keys %subs) . ")\b";
     while (<<>>) {
       print s/$subre/$subs{$&}/egr;
     }
' tab_sep_file.txt original_file.txt

(这个会在要替换的词中转义正则表达式元字符,使其更健壮)