使用正则表达式匹配带有特定变音符号的字符

7

在正则表达式中,有没有一种方法可以指定一个带有特定变音符号的字符匹配?例如,重音符号。做这件事的长方法是去维基百科上查看重音符号页面,复制它显示的所有字符,然后将它们制作成一个字符类:

/[àầằèềḕìǹòồṑùǜừẁỳ]/i

这相当繁琐。我原本希望能够使用Unicode属性,例如\p{hasGraveAccent},但是我找不到类似的东西。搜索解决方案只会显示那些试图匹配字符而忽略变音符号的人们提出的问题,这需要执行某种规范化过程,这不是我想要的。


如果它是一个组合字符,那么可以通过生成Unicode代码点列表来实现。 - kba
将单个字母制作成字符类是不可靠的,并且不起作用。它仅适用于匹配字符串 NFC(组合规范化形式)的预组合字母。大多数具有两个或更多变音符号的字符没有预组合字符。即它们由超过一个代码点(在 Unicode 语言中是字符)组成。如果您将它们复制并粘贴到字符类中,那么变音符号仍然是一个单独的字符,并且将与目标字符串中相同的单个变音符号匹配。 - Helmut Wollmersdorfer
2个回答

1

有一些限制,但是可以实现。

#!perl

use strict;
use warnings;

use Encode;
use Unicode::Normalize;
use charnames qw();
use utf8;  # source is utf-8

binmode(STDOUT, ":utf8"); # print in utf-8

my $utf8_string = 'xàaâèaêòͤ';

my $nfd_string = NFD($utf8_string); # decompose

my @chars_with_grave = $nfd_string =~
  m/
    (
      \p{L}           # one letter
      \p{M}*          # 0 or more marks
      \N{COMBINING GRAVE ACCENT}
      \p{M}*          # 0 or more marks
    )
  /xmsg;

print join(', ',@chars_with_grave), "\n";

这将打印

$ perl utf_match_grave.pl 
à, è, òͤ

注意:编辑区中的字符正确地显示为组合形式,但stackoverflow会错误地将它们分开显示。

需要一个字母作为基字符。更改正则表达式以适用于其他基字符。标记\p{M}可能不完全符合您的要求,需要改进。


0
这是一个有点棘手的问题,但是它是可以解决的。首先,您必须将Unicode字符串规范化为4种形式之一。关于规范化的信息在这里,包含不同规范化的字符示例映射在这里,规范化字符的好图表在这里。基本上,规范化只是确保处理变音符时所有字符都处于相同的格式中。Golang对此有很好的支持,大多数语言应该都包含可执行此操作的库。
因此,对于我的示例,请将字符串转换为“规范化形式D”(NFD)和utf32,以便所有Unicode字符都是其4字节代码点。
所有重音符号的变音符字符都在字符旁边有0x0300。因此,您可以在ascii模式下(而不是unicode模式下)进行正则表达式搜索....\x00\x00\x03\x00。从那里,您需要提取它所在的符文位置。这可以使用不同的方法完成,具体取决于您使用的编码方式。

所以如果你落在4的除数上,你就知道它是一个有效的字符。

除此之外,没有官方的Perl字符分组来做到这一点。

以下是Perl代码示例:

use Encode;
use Unicode::Normalize;

$StartUTF8='xàaâèaê';
$PerlEncoded=decode('utf8', $StartUTF8);
$PerlNormalized=NFD($PerlEncoded); 
$UTF32Normalized=encode('utf32', $PerlNormalized);

while($UTF32Normalized =~ /(....\x00\x00\x03\x00)/gs) {
    $Pos=pos($UTF32Normalized)-8;
    if($Pos%4==0) {
        print("$Pos\n");
    }
}

但是此时,你可能只是在字符上执行for循环 :-\

我还尝试过使用//c进行匹配而不需要位置测试,但出于某种原因它不起作用。

/^(?:....)*?(....\x00\x00\x03\x00)/gcs


没有必要转换为UTF32(如果您假设结果是UTF-32LE,则应该转换为该格式,而不是留给机会)。此外,当字形包含多个变音符号时,假定重音符号紧随基字符之后的假设可能是不正确的。 - rici
确实。这次研究和测试相当无果。 - Dakusan

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接