我需要解析一个大文件,替换可能出现的子字符串,这些子字符串是与正则表达式匹配的可能出现的字符串的一部分,其值取自索引是相关子字符串的数组。
该文件是一个常规文本文件,即由换行符分隔的行,每行可以包含 ASCII 32 和 ASCII 126 之间的任意字符,基本上是除 C 语言环境中的控制字符之外的任何可打印字符。
精确匹配感兴趣的字符串的扩展正则表达式是\<prefix-[[:alnum:]]{2,}\>
,而有问题的子字符串是破折号后面的任何内容。
使用样本(合成的)输入,例如:
# arbitrary number of comment lines of any length
:prefix-foo ; arbitrary strings
# arbitrary number of comment lines of any length foo -prefix-foo-
-bar -foo-xx arbitrary string -yet-more strings prefix-foo-bar MORE strings
YET more --STRINGS prefix-bar -prefix-foo-STRingS--
even MORE strings ; prefix -foo -yy--more-and-prefix-bar-and-more
并有一个示例字典,例如:
dictionary["foo"] = 2
dictionary["bar"] = 15
期望的输出是:
# arbitrary number of comment lines of any length
:prefix-2 ; arbitrary strings
# arbitrary number of comment lines of any length foo -prefix-2-
-bar -foo-xx arbitrary string -yet-more strings prefix-2-bar MORE strings
YET more --STRINGS prefix-15 -prefix-2-STRingS--
even MORE strings ; prefix -foo -yy--more-and-prefix-15-and-more
我认为这将是最好的工具,特别是因为它本身就能够通过替换单个字段awk
来重写整个记录。因此我想出了以下脚本:$0
$1...$n
#!/usr/bin/gawk -f
BEGIN {
# first fill in dictionary
while ("cmd-providing-dictionary" | getline) {
dictionary[$1] = $2
}
close("cmd-providing-dictionary")
# pattern that matches interesting fields
field_regex = "\\<prefix-[[:alnum:]]{2,}\\>"
# I don't care default splitting of line
FS = OFS = ""
}
{
# split line in fields as per regex
if (patsplit($0, fields, field_regex, seps)) {
FS = OFS = "-"
# for each field, split it on dash character,
# modify its substring as per dictionary,
# and finally rebuild it
for (fn in fields) {
$0 = fields[fn]
if ($2 in dictionary) {
$2 = dictionary[$2]
fields[fn] = $0
}
}
FS = OFS = ""
# clear whole record and rebuild it with
# fields computed above + original separators
$0 = ""
for (fn in fields)
$fn = seps[fn - 1] fields[fn]
$(fn+1) = seps[fn]
}
print
}
尽管我对 awk 的使用能力不强,但上面的代码似乎足够快地完成了正确的工作,但看起来有点笨拙,感觉就像我强迫awk
以一种不自然的方式做事。我想知道是否有更好的方法来获得相同的结果。或者也是一个更好的工具。
gsub()
我的第一个想法是使用or进行简单的正则表达式替换gensub()
,但我发现没有(干净的)方法来使用正则表达式的子表达式(这将是\<prefix-([[:alnum:]]{2,})\>
)作为查找数组并在替换字符串中使用该值的键。另一方面,循环遍历所有字典键以始终应用所有gsub
s 并不是真正可行,因为字典非常大,因此效率非常低。
答案1
只是为了进行比较,这里有一个非专家的 Perl 版本,它通过能够从替换中调用函数而获得了很多好处。就好像你可以说
gsub(regexp, call_function(matched_part), variable_to_change)
其中函数返回替换字符串。
#!/usr/bin/perl
use strict;
my %d;
sub fix{
my ($prefix,$str) = @_;
$str = $d{$str} if defined $d{$str};
return "$prefix$str";
}
open(D,"dictionary") or die;
while(<D>){
$d{$1} = $2 if $_ =~ m/^([^ ]+) ([^ \n]+)/;
}
close(D);
while(<>){
$_ =~ s/\b(prefix-)([[:alnum:]]{2,})\b/fix($1,$2)/ge;
print;
}
此处,替换命令全局$_ =~ s/regex/fix($1,$2)/ge
更改当前行$_
(g),并执行(e)替换字符串fix()
,其中$1
和是正则表达式的$2
捕获组(在内)。()