如何替换字符串以避免进行二阶替换?

时间:2018-08-01 11:57:05

标签: perl awk sed

我有一个文本文件,我想替换文本。但是,这次我的问题是新文本还包括原始存在于原始文件中并出现在替换哈希中的文本。

如何替换文本,但每行只能替换一次,从而避免进行“二阶”替换?

通常我用sed进行替换,其中我使用col1和col2文件进行替换。 col1具有要替换的原始字符串; col2新的。

输入文件:

ID1 X1 X2 X3
ID2 X3 X4 X5
col1    col2 of "hash" used for replacement:
X1      X2 X3
X2      X7
X3      X8

替换为sed后的输出

ID1 X7 X3 X7 X8
ID2 X3 X4 X5

预期输出

ID1 X2 X3 X7 X8
ID2 X8 X4 X5

使用了bash命令

paste col1 col2 | while read n k; do sed -i \"\" \"s/$n/$k/g\" input; done

4 个答案:

答案 0 :(得分:1)

下面是一些Perl代码,它从repl.txt中读取替换哈希。看起来像

repl.txt

X1      X2 X3
X2      X7
X3      X8

DATA文件句柄读取输入。您可以通过打开自己的文件来轻松修改它。最简单的方法是在命令行上将输入文件的路径指定为参数。那么您只需将<DATA>更改为<>:无需显式打开

repl.pl

use strict;
use warnings 'all';

# Read the hash from `repl.txt`
my %repl = do {
    open my $fh, '<', 'repl.txt' or die $!;
    map { chomp; split ' ', $_, 2; } <$fh>;
};

# Build and compile regex pattern
my $re = join '|', map { "\\b$_\\b" } keys %repl;
$re = qr/$re/;

while ( <DATA> ) {
    s/($re)/$repl{$1}/g;
    print;
}

__DATA__
ID1 X1 X2 X3
ID2 X3 X4 X5

输出

ID1 X2 X3 X7 X8
ID2 X8 X4 X5

更新

如果您希望使用两个单独的文件作为哈希的键和值,则可以像这样更改哈希的加载

col1

X1
X2
X3

col2

X2 X3
X7
X8

用于加载哈希%repl

的代码
my %repl;
{
    my $fh;

    open $fh, '<', 'col1' or die $!;
    my @keys = map { chomp; $_; } <$fh>;

    open $fh, '<', 'col2' or die $!;
    my @vals = map { chomp; $_; } <$fh>;

    @repl{@keys} = @vals;
}

答案 1 :(得分:0)

在第一次替换中,在字符串周围添加一些“后卫”字符,否则,使其成为唯一。然后,让您的第二个替换模式忽略此类受保护的令牌,最后删除保护。

例如,如果要将“ A”替换为“ B”,将“ B”替换为“ A”,则可以将“ A”替换为“ _A_”,将“ B”替换为“ A”,然后将“ _A_”替换为“ B”。

答案 2 :(得分:0)

sed仅用于单行上的简单替换,您不应使用shell循环来操纵文本(请参见why-is-using-a-shell-loop-to-process-text-considered-bad-practice)。对于其他任何事情,您都应该使用awk来实现简单性,清晰度,鲁棒性,效率,可移植性等。

$ awk '
    NR==FNR { map[$1]=$2; next }
    { for (i=1;i<=NF;i++) $i=($i in map ? map[$i] : $i); print }
' FS='\t' repl.txt FS=' ' file
ID1 X2 X3 X7 X8
ID2 X8 X4 X5

以上内容将在任何UNIX系统上的任何Shell中使用任何awk来稳定有效地工作。请注意,它使用的是文字字符串,因此,如果/当您的旧字符串或新字符串包含regexp或向后引用元字符或任何其他字符时,它不会失败,这与问题中的sed脚本不同。

上面使用的输入文件如下,repl.txt中的旧值和新值用制表符分隔:

$ cat repl.txt
X1      X2 X3
X2      X7
X3      X8

$ cat file
ID1 X1 X2 X3
ID2 X3 X4 X5

答案 3 :(得分:0)

这可能对您有用(GNU sed):

sed -r '1d;s#(\S+)\s*(.*)#s/\\n\1\\n/\2/#' replacementFile |
sed -re 's/\S+/\n&\n/g' -f - -e 's/\n//g' inputFile

使用replacementFile创建sed脚本,并将其与一些样板sed代码结合起来。

第一组sed命令创建sed替换命令,其中LHS是要替换的值,而RHS是要替换的值。 LHS被换行符包围。

第二组sed命令,首先用换行符包围所有值,然后使用第一组sed命令中的脚本,最后删除换行符。

由于期望所有值都被换行符包围,而替换的值不会被换行符包围,因此对于替换错误的值不会产生混淆。