微信公众号搜"智元新知"关注
微信扫一扫可直接关注哦!

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

如何解决是否可以修改单词字符类或\ 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 举报,一经查实,本站将立刻删除。