在两个特定位置的多个文件中执行多项替换之一

在两个特定位置的多个文件中执行多项替换之一

我有大约十万个文件,对于每个文件我想执行以下操作:

在具有以下 ascii 代码的文件中的第五个和第六个字符之间0x1f存在一个字符串。现在我希望应用程序打开一个包含所有替换列表的特定文件,请参阅稍后该替换文件的格式。如果替换不在文件中,请将文件名写入 stderr,以便我稍后可以手动修复该文件。现在在ascii代码的第16个和第17个字符之间,0x1f要替换的内容将再次出现,但是这次字段不仅仅是要替换的内容,而是一串包含要替换的内容的html字符串,可以替换一次到多次。我只想替换该字段中的第一次出现。

替换文件的格式相当简单,每个替换都占自己的一行,并且用空格分隔。它们是按照要替换的东西的长度排序的。


例子

替换文件:

CCCC 3
BCC 233
CCA 331
CCB 332
ACC 133
AA 11
AB 12
BA 21
BB 22
CC 33
A 1
B 2

请注意,它不保证是上面那样的字符和数字,这只是一个示例,可能包含 UTF-8。

文件:(以下示例中0x1f字符写为^_)

field1^_field2^_field3^_field4^_field5^_BB^_field7^_hai
this field contains a newline^_some UTF-8オイ^_the next field is empty^_^_
another newline^_field14^_field15^_<b>BB</b>stuff BB^_the previous field contains something to replace^_^_^_more fields...

该文件将变成

field1^_field2^_field3^_field4^_field5^_22^_field7^_hai
this field contains a newline^_some UTF-8オイ^_the next field is empty^_^_
another newline^_field14^_field15^_<b>22</b>stuff BB^_the previous field contains something to replace^_^_^_more fields...

我已经上传了我输入的真实示例这里。该文件所需的输出是这里RYO应替换为リョ)。


一点背景
某个白痴决定不在我们的数据库中创建单独的列,而是创建一个列并用 0x1f 字符分隔字段。他还认为可以在两个不同的字段中复制我想要更改的信息。我将数据库中的信息提取到文件 pr 中。行只包含带有字段的列,因为我怀疑这更容易使用,但是如果你可以做出一个我可以给 SQLite 数据库的语句,那也很好。

答案1

这个 Perl 脚本应该可以做到。我在您在pastebin上的示例上进行了测试,它按预期工作:

#!/usr/bin/env perl
use strict;

my %k; ## This hash will store the target/replacement pairs
## Read the list of replacements
open(my $r,"$ARGV[0]")||die "Couldn't open replacements list\n";
while(<$r>){
    chomp;
    my @F=split(/\s+/);
    $k{$F[0]}=$F[1]
}
close($r);
$/=undef;

open(my $fh, "$ARGV[1]")||die "Couldn't open input file\n";
while(<$fh>){
    ## Read the entire file at once
    $/=undef;
    my @F=split(/\x1f/);
    ## If this exists in the replacements list
    if (defined($k{$F[5]})) {
        ## Modify the 17th field. This will only replace the first
        ## occurence. Use 's///g' for all. 
        $F[16]=~s/$F[5]/$k{$F[5]}/;
        ## Replace the 6th field
        $F[5]=$k{$F[5]};

    }
    ## If it doesn't
    else {
        ## Print the file name to STDERR unless the 5th field
        ## was empty.
        print STDERR "Problematic file: $ARGV[1]\n" unless $F[5]=~/^\s*$/;
    }
    ## print each field separated by '0x1f' again.
    print join "\x1f",@F;


}
close($fh);

将其保存fixidiocy.pl在您的$HOME目录中,并cd保存到包含目标文件的目录中。现在,在每个文件上运行它,并给出文件名和替换文件的路径作为参数:

for file in *; do 
    perl ~/fixidiocy.pl /path/to/replacements "$file" > "$file".fixed
done

相关内容