regex - 使用Unicode字符属性时如何模拟单词边界?

标签 regex perl unicode utf-8 locale

从我以前的问题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直接相反,因此,我们可以翻转正/负环顾四周来模拟效果。这也是有道理的-只有在前后有更多字符时才能形成无边界。

其他模拟(让cC的补码字符类):
  • \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/

    相关文章:

    unicode - Python 3 不读取新服务器上的 unicode 文件

    python 2.7 re.MULTILINE 问题

    javascript - 如何编写正则表达式来匹配不跟随另一个 "\n"的 "\n"?

    regex - 我想匹配任何不包括 '#' 但包括 '\#' 的内容

    javascript - 从变量中删除括号和尾随空格

    java - Java中的字节和字符转换

    perl - Perl 中什么情况下会跳过 END block ?

    perl - 使用 AnyEvent::Handle 和 tcp_connect 重新连接

    regex - Perl 中用于电子邮件拆分的正则表达式

    c# - 在控制台窗口中写入希伯来字符,C#