从我以前的问题Why under locale-pragma word characters do not match?和How to change nested quotes中,我了解到,在处理UTF-8数据时,您不能将\w
视为word-char,并且必须使用Unicode字符属性\p{Word}
。现在,我发现零宽度字边界\b
也不适用于UTF-8(启用了语言环境),但是在Unicode字符属性中找不到任何等效项。我以为自己可以自己构造它:(?<=\P{Word})(\p{Word}+)(?=\P{Word})
,它应该等效于\b(\w+)\b
。
在下面的测试脚本中,我有两个数组来测试两个不同的正则表达式。如果未启用语言环境,则第一个基于\b
的文件可以正常工作。为了使它也能与语言环境一起使用,我编写了另一个版本来模拟边界(?=\P{Word})
,但它没有按我预期的那样工作(我也在脚本中显示了预期的结果)。
您是否看到问题所在?如何首先使用ASCII(或不使用语言环境)来模拟正则表达式?
#!/usr/bin/perl
use 5.010;
use utf8::all;
use locale; # et_EE.UTF-8 in my case
$| = 1;
my @test_boundary = ( # EXPECTED RESULT:
'"abc def"', # '«abc def»'
'"abc "d e f" ghi"', # '«abc «d e f» ghi»'
'"abc "d e f""', # '«abc «d e f»»'
'"abc "d e f"', # '«abc "d e f»'
'"abc "d" "e" f"', # '«abc «d» «e» f»'
# below won't work with \b when locale enabled
'"100 Естонiï"', # '«100 Естонiï»'
'"äöõ "ä õ ü" ï"', # '«äöõ «ä õ ü» ï»'
'"äöõ "ä õ ü""', # '«äöõ «ä õ ü»»'
'"äöõ "ä õ ü"', # '«äöõ «ä õ ü»'
'"äöõ "ä" "õ" ï"', # '«äöõ «ä» «õ» ï»'
);
my @test_emulate = ( # EXPECTED RESULT:
'"100 Естонiï"', # '«100 Естонiï»'
'"äöõ "ä õ ü" ï"', # '«äöõ «ä õ ü» ï»'
'"äöõ "ä õ ü""', # '«äöõ «ä õ ü»»'
'"äöõ "ä õ ü"', # '«äöõ "ä õ ü»'
'"äöõ "ä" "õ" ï"', # '«äöõ «ä» «õ» ï»'
);
say "BOUNDARY";
for my $sentence ( @test_boundary ) {
my $quote_count = ( $sentence =~ tr/"/"/ );
for ( my $i = 0 ; $i <= $quote_count ; $i += 2 ) {
$sentence =~ s/
"( # first qoute, start capture
[\p{Word}\.]+? # suva word-char
.*?\b[\.,?!»]*? # any char followed boundary + opt. punctuation
)" # stop capture, ending quote
/«$1»/xg; # change to fancy
}
say $sentence;
}
say "EMULATE";
for my $sentence ( @test_emulate ) {
my $quote_count = ( $sentence =~ tr/"/"/ );
for ( my $i = 0 ; $i <= $quote_count ; $i += 2 ) {
$sentence =~ s/
"( # first qoute, start capture
[\p{Word}\.]+? # at least one word-char or point
.*?(?=\P{Word}) # any char followed boundary
[\.,?!»]*? # optional punctuation
)" # stop capture, ending quote
/«$1»/gx; # change to fancy
}
say $sentence;
}
最佳答案
由于\b
位置后面的字符是一些标点符号或"
(为安全起见,请仔细检查\p{Word}
与它们中的任何一个都不匹配),因此该字符属于\b\W
大小写。因此,我们可以使用以下命令模拟\b
:
(?<=\p{Word})
我对Perl不熟悉,但是从what I tested here来看,当编码设置为UTF-8时,
\w
(和\b
)似乎也能很好地工作。$sentence =~ s/
"(
[\w\.]+?
.*?\b[\.,?!»]*?
)"
/«$1»/xg;
如果您升级到Perl 5.14及更高版本,则可以使用
u
标志将字符集设置为Unicode。您可以使用这种通用策略来构造与字符类相对应的边界。 (就像
\b
单词边界定义如何基于\w
的定义一样)。令
C
为字符类。我们想定义一个基于字符类C的边界。当您知道当前字符属于
C
字符类(等效于(\b\w)
)时,下面的构造将模拟前面的边界:(?<!C)C
或后面(相当于
\w\b
):C(?!C)
为什么会有负面的环顾四周?因为正向环视(带有互补字符类)还将断言在前面/后面必须有一个字符(在前面/后面至少保持1个宽度)。负向查找将允许在不编写繁琐的正则表达式的情况下开始/结束字符串。
对于
\B\w
仿真:(?<=C)C
和类似
\w\B
:C(?=C)
\B
与\b
直接相反,因此,我们可以翻转正/负环顾四周来模拟效果。这也是有道理的-只有在前后有更多字符时才能形成无边界。其他模拟(让
c
为C
的补码字符类):\b\W
:(?<=C)c
\W\b
:c(?=C)
\B\W
:(?<!C)c
\W\B
:c(?!C)
对于独立边界的仿真(等效于
\b
):(?:(?<!C)(?=C)|(?<=C)(?!C))
和独立的无边界(相当于
\B
):(?:(?<!C)(?!C)|(?<=C)(?=C))
关于regex - 使用Unicode字符属性时如何模拟单词边界?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/14942652/