如何解决是否可以修改单词字符类或\ b边界以排除下划线字符?
我需要替换一大堆预定义模式。 这些模式只能包含[a-zA-Z]字符,不包括下划线。 这些模式可能以不同的形式出现:作为一个完整的单词或一个单词,在单词之前和/或之后跟一个非降级字符'_'
示例:我想用BAR代替FOO 我使用以下4条说明
$ cat > /tmp/try.pl
s/\bFOO\b/BAR/g;s/\bFOO_/BAR_/g;s/_FOO\b/_BAR/g;s/_FOO_/_BAR_/g;
$ perl -p /tmp/try.pl
FOO aaa_FOO FOO_bbb FOO.txt a-FOO-b.txt aaa_FOO_bbb dontchange_FOOQUX_dontchange
BAR aaa_BAR BAR_bbb BAR.txt a-BAR-b.txt aaa_BAR_bbb dontchange_FOOQUX_dontchange
这正是我想要的。但是用成千上万的单词需要时间。 如果我可以从单词字符类中排除下划线,我认为我只能使用一条指令:
s/\bFOO\b/BAR/g.
那么有什么方法可以修改perl世界字符类或/ b边界定义以排除下划线字符?
解决方法
更新
已阐明要替换的单词是给定列表中的文字字符串(无需匹配[a-zA-Z]
),然后使用由这些单词构建的替换。†此外,这些单词中的每一个都需要替换为一个预先定义的给定模式。为此使用哈希。
我假设一个单词的任何一侧都不能被_
或单词边界以外的任何东西包围。为此,可以使用lookarounds
一个测试程序
use warnings;
use strict;
use feature 'say';
my @words_to_replace = qw(one ones thing nothing clean);
my %replacement = map { $_ => 'NEW.'.$_ } @words_to_replace;
my $re_word = join '|',@words_to_replace; # no quotemeta; only [a-zA-Z]
my @test = qw(noone ones_ athing _thing nothing. _nothing_ clean);
for (@test) {
printf "For %-12s: ","|$_|";
if ( s{ (?<! [^_\W]) ($re_word) (?! [^_\W]) }{$replacement{$1}}x ) {
say "mathced |$1|,now have |$_|";
}
else { say '' }
}
我通过在每个词后附加NEW.
来组成一个替换词。按预期打印。
环顾四周规定,单词不得用_
或\W
(字符边界)以外的任何其他字符包围。那里令人讨厌的三重否定(不是不是非单词边界字符的任何东西)是一种在环视中也考虑零宽度锚点的方法。
†如果所获得的模式长于大约32k左右的字符,则用(“ 千”个)单词构建的替换对于正则表达式可能会造成问题。如果您的列表确实太长,以至于$re_word
的长度超过了这个数字,那么也许最经济的方法是将列表分成多个足够小的列表,并对每个列表进行上述操作。 (尝试一次匹配和替换一个单词会慢很多。)
原始回复 (认为我们需要匹配[a-zA-Z]
,并且只能匹配_
)>
一种方法是使用POSIX character classes,其中[[:alpha:]]
与[a-zA-Z]
匹配
我尚不清楚什么是通用词的替代品,但是一旦给出
s/([[:alpha:]]+)/$replacement/;
另一种方法是根据需要形成图案并使用它
my $re_char = qr/[a-zA-Z]/;
s/($re_char+)/$replacement/;
请说明替换的工作方式(foo-bar语言除外)。
如果替换本身并不重要,但是仅当匹配的单词可能仅在两边被_
包围时才需要执行替换操作,所以可以使用lookarounds来排除{ {1}}
_
(编辑— ”要添加单词边界,请改用m/(?<! [^_] )( [[:alpha:]]+ ) (?! [^_]) /x;
。请参见第一部分)
一个测试程序
[^_\W]
这将匹配单词(use warnings;
use strict;
use feature 'say';
my @words = qw(_before _. after_ _both_ none .ahem nah/);
for (@words) {
printf "%-8s:\t",$_;
if ( m/(?<! [^_] )( [[:alpha:]]+ ) (?! [^_]) /x ) {
say $1;
}
else { say "... no match" }
}
)的两边或下划线,或者周围没有下划线,但匹配带有其他字符([a-zA-Z]
和.
)的单词。
(编辑— 要与/
一起使用单词边界,请使用_
。请参见第一部分)
您可以将\b
和_
合并到捕获组(\b|_)
中,然后将正则表达式合并为一个:
s/(\b|_)FOO(\b|_)/${1}BAR$2/g;
这是使用原始替换的功能,但是正如ikegami在评论中指出的那样,这将失败,例如_FOO_FOO_
。我们可以使用环视断言来解决此问题:
s/(?:\b|_)\KFOO(?=\b|_)/BAR/g
这对我们的边框字符没有破坏性,因此可以匹配由单个边框字符分隔的两个替换字符,例如_FOO_FOO_
。
除了底线之外,您还想排除更多内容。 \w
可以匹配29,511个字符,比您认为的53个字符还要小。
您可以使用
my %repl = ( FOO => "BAR" );
s{[a-zA-Z]+}{ $repl{$&} // $& }eg
或
s/(?<![a-zA-Z])FOO(?![a-zA-Z])/BAR/g
后面是后者的解释,接着是标题问题的答案。
\b
等同于
(?: (?<!\w)(?=\w) # At the beginning of a word
| (?<=\w)(?!\w) # At the end of a word
)
我们希望将\w
替换为[a-zA-Z]
。
(?: (?<![a-zA-Z])(?=[a-zA-Z])
| (?<=[a-zA-Z])(?![a-zA-Z])
)
所以
\bFOO\b
将被替换为
(?: (?<![a-zA-Z])(?=[a-zA-Z])
| (?<=[a-zA-Z])(?![a-zA-Z])
)
FOO
(?: (?<![a-zA-Z])(?=[a-zA-Z])
| (?<=[a-zA-Z])(?![a-zA-Z])
)
赞!值得庆幸的是,因为我们知道FOO
的开头和结尾都是与[a-zA-Z]
匹配的字符,所以可以简化它!
(?<![a-zA-Z])FOO(?![a-zA-Z])
修改\w
以排除下划线
您可以使用
[^\W_] # \w is equivalent to [^\W]
或
(?[ \w - [_] ]) # Experimental
修改\b
以排除下划线
您可以使用(?<![^\W_])FOO(?![^\W_])
代替如上所述的\bFOO\b
。
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。