gpt4 book ai didi

regex - 正则表达式以匹配不同Unicode脚本之间的边界

转载 作者:行者123 更新时间:2023-12-04 08:44:55 26 4
gpt4 key购买 nike

正则表达式引擎的概念是“零宽度”匹配,其中一些对于查找单词的边缘非常有用:

  • \b-存在于大多数引擎中,以匹配单词和非单词字符之间的任何边界
  • \< and \> - present in Vim分别仅匹配单词开头和单词结尾处的边界。

  • 在某些正则表达式引擎中,较新的概念是Unicode类。脚本是其中的一种,它可以区分拉丁语,希腊语,西里尔字母等。这些示例都是等效的,并且可以匹配希腊书写系统的任何字符:
  • \p{greek}
  • \p{script=greek}
  • \p{script:greek}
  • [:script=greek:]
  • [:script:greek:]

  • 但是到目前为止,在阅读有关正则表达式和Unicode的资料时,我还无法确定是否有任何标准或非标准的方法来实现零宽度匹配,其中一个脚本结束而另一个脚本开始。

    在字符串 παν語中, ν字符之间将有一个匹配项,就像 \b\<将在 π字符之前进行匹配一样。

    现在,在本示例中,我可以根据查找 \p{Greek}\p{Han}的方式将某些内容一起黑客入侵,甚至可以根据两个Unicode脚本名称的所有可能组合将某些内容一起入侵黑客。

    但这不是确定性的解决方案,因为新脚本仍在每个发行版中都添加到Unicode中。 是否有表达这种观点的面向 future 的方法?还是有添加提案?

    最佳答案

    编辑:我刚刚注意到您实际上并没有指定您使用的是哪种模式匹配语言。好吧,我希望Perl解决方案对您有用,因为在任何其他语言中,所需的机械装置可能真的很难。另外,如果您要使用Unicode进行模式匹配,那么Perl实际上是针对特定工作的最佳选择。

    当下面的$rx变量设置为适当的模式时,Perl代码的这个小片段:

    my $data = "foo1 and Πππ 語語語 done";

    while ($data =~ /($rx)/g) {
    print "Got string: '$1'\n";
    }

    生成此输出:

    Got string: 'foo1 and '
    Got string: 'Πππ '
    Got string: '語語語 '
    Got string: 'done'

    即,它拉出一个拉丁字符串,一个希腊字符串,一个汉字符串和另一个拉丁字符串。这完全不符合我认为您的实际需求。

    我昨天未发布此消息的原因是,我收到了奇怪的核心转储信息。现在我知道为什么了。

    我的解决方案在 (??{...})构造内部使用词法变量。事实证明,在v5.17.1之前这是不稳定的,充其量只能是偶然的。它在v5.17.0上失败,但在v5.18.0 RC0和RC2上成功。因此,我添加了一个 use v5.17.1以确保您正在运行的内容足够新,可以信任此方法。

    首先,我确定您实际上并不想运行所有相同的脚本类型;您需要运行所有相同脚本类型 以及 Common和Inherited。否则,您会被标点符号,空格,Common的数字和组合字符的Inherited搞砸了。我真的不希望您希望这些内容中断您“全部相同的脚本”的运行,但是如果您这样做了,就很容易停止考虑这些内容。

    因此,我们要做的是前瞻查找具有非Common或Inherited脚本类型的第一个字符。不仅如此,我们还从中提取出该脚本类型的实际含义,并使用此信息构建一个新模式,该模式可以是任意数量的字符,其脚本类型可以是Common,Inherited或我们刚刚发现并保存下来的任何脚本类型。然后,我们评估该新模式并继续。

    嘿,我 这是多毛的,不是吗?

    在我要显示的程序中,我留下了一些注释掉的调试语句,这些语句仅显示其作用。如果您取消注释,则将在上一次运行时获得以下输出,这应该有助于理解该方法:

    DEBUG: Got peekahead character f, U+0066
    DEBUG: Scriptname is Latin
    DEBUG: string to re-interpolate as regex is q{[\p{Script=Common}\p{Script=Inherited}\p{Script=Latin}]*}
    Got string: 'foo1 and '
    DEBUG: Got peekahead character Π, U+03a0
    DEBUG: Scriptname is Greek
    DEBUG: string to re-interpolate as regex is q{[\p{Script=Common}\p{Script=Inherited}\p{Script=Greek}]*}
    Got string: 'Πππ '
    DEBUG: Got peekahead character 語, U+8a9e
    DEBUG: Scriptname is Han
    DEBUG: string to re-interpolate as regex is q{[\p{Script=Common}\p{Script=Inherited}\p{Script=Han}]*}
    Got string: '語語語 '
    DEBUG: Got peekahead character d, U+0064
    DEBUG: Scriptname is Latin
    DEBUG: string to re-interpolate as regex is q{[\p{Script=Common}\p{Script=Inherited}\p{Script=Latin}]*}
    Got string: 'done'

    最后,这是一件大事:
    use v5.17.1;
    use strict;
    use warnings;
    use warnings FATAL => "utf8";
    use open qw(:std :utf8);
    use utf8;

    use Unicode::UCD qw(charscript);

    # regex to match a string that's all of the
    # same Script=XXX type
    #
    my $rx = qr{
    (?=
    [\p{Script=Common}\p{Script=Inherited}] *
    (?<CAPTURE>
    [^\p{Script=Common}\p{Script=Inherited}]
    )
    )
    (??{
    my $capture = $+{CAPTURE};
    #####printf "DEBUG: Got peekahead character %s, U+%04x\n", $capture, ord $capture;
    my $scriptname = charscript(ord $capture);
    #####print "DEBUG: Scriptname is $scriptname\n";
    my $run = q([\p{Script=Common}\p{Script=Inherited}\p{Script=)
    . $scriptname
    . q(}]*);
    #####print "DEBUG: string to re-interpolate as regex is q{$run}\n";
    $run;
    })
    }x;


    my $data = "foo1 and Πππ 語語語 done";

    $| = 1;

    while ($data =~ /($rx)/g) {
    print "Got string: '$1'\n";
    }

    是的,应该有一个更好的方法。我认为还没有。

    所以现在,享受。

    关于regex - 正则表达式以匹配不同Unicode脚本之间的边界,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/16492933/

    26 4 0
    Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
    广告合作:1813099741@qq.com 6ren.com