Perl 是否可以修改word字符类或\b边界以排除下划线字符?

Perl 是否可以修改word字符类或\b边界以排除下划线字符?,perl,word-boundary,Perl,Word Boundary,我需要替换一个非常大的预定义模式列表。 这些模式只能包含[a-zA-Z]字符,不包括下划线。 这些模式可能以不同的形式出现:作为一个完整的单词或单词,前面和/或后面有一个不得分的字符“\u1” 示例:我想用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_b

我需要替换一个非常大的预定义模式列表。 这些模式只能包含[a-zA-Z]字符,不包括下划线。 这些模式可能以不同的形式出现:作为一个完整的单词或单词,前面和/或后面有一个不得分的字符“\u1”

示例:我想用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
这正是我想要的。但是千言万语需要时间。 如果我可以从word字符类中排除下划线,我想我只能使用一条指令:

s/\bFOO\b/BAR/g.

那么,有没有办法修改perl世界字符类或/b边界定义以排除下划线字符?

您只需将捕获组中的
\b
(\b |)合并为一个正则表达式:

s/(\b|_)FOO(\b|_)/${1}BAR$2/g;
这是使用原始替换的功能,但正如ikegami在评论中指出的,这将失败,例如
\u FOO\u FOO
。我们可以使用lookaround断言修复此问题:

s/(?:\b|_)\KFOO(?=\b|_)/BAR/g

这对我们的边界字符是无损的,因此可以匹配由单个边界字符分隔的两个替换,例如在
\u FOO\u FOO\u
更新的情况下

需要说明的是,要替换的单词是给定列表中的文字字符串(无需匹配
[a-zA-Z]
)——然后使用这些单词构建的替换。此外,这些单词中的每一个都需要替换为预定义的给定模式。使用散列

我假设一个单词不能被任何东西包围,除了两边可能有一个
\u
或单词边界。为此,我们可以使用

测试程序

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 '' }
}
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" }
} 
另一种方法是形成一个你喜欢的模式并使用它

my $re_char = qr/[a-zA-Z]/;

s/($re_char+)/$replacement/;
请澄清替换应如何工作(foo bar语言除外)

如果替换本身无关紧要,但仅当匹配的单词可能在两侧仅被
\uu
包围时才需要进行替换,则可以使用来排除
\u
以外的任何字符

m/(?<! [^_] )( [[:alpha:]]+ ) (?! [^_]) /x;
这将匹配单词(
[a-zA-Z]
)的每一面或两面都有下划线,或者周围没有任何内容,但不匹配周围有其他字符的单词(
/


编辑-使用
[^\uw]
允许单词边界和
一起使用
[^\uw]
。请参见第一部分)

您想要排除的不仅仅是下划线<代码>\w
匹配29511个字符,比您认为匹配的53个字符多一点

你可以用

my%repl=(FOO=>“BAR”);
s{[a-zA-Z]+}{$repl{$&}/$&}例如

s/(?)?
后面是对后者的解释,标题问题的答案如下


\b
相当于

(?:(?)?
我们想用
[a-zA-Z]
替换
\w

(?:(?)?
所以

\bFOO\b
将被替换为

(?:(?)?
哎呀!谢天谢地,因为我们知道
FOO
都以匹配
[a-zA-Z]
的字符开头和结尾,所以这可以简化

(?

修改
\w
以排除下划线

你可以用

[^\W_u3;]\W等同于[^\W]

(?[\w-[[u]])实验性

修改
\b
以排除下划线


您可以使用
(?而不是如上所述的
\bFOO\b

事实上,我需要替换一个不能用regexp表示的预定义单词列表。每个预定义单词都必须替换为另一个唯一的模式。@Philippergegioire相应地更新并重新排列,我不确定我是否可以将\b与其他字符组合。解决方案不会重放e行中的所有模式,只有第三个:aaa_FOO_bbb。谢谢你的想法。@PhilippeGregoire似乎
\b
在字符类括号内不起作用。不用担心,我们可以用
代替。编辑答案。
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" }
}