java - 假设Unicode和不区分大小写,模式 “..”是否应匹配 “FfIsS”?

标签 java regex unicode case-insensitive case-folding

这听起来像个 Jest ,但我可以证明这一点。

假设:

  • 点匹配任何单个字符。
  • 不区分大小写的模式仅当与s匹配时才与s.toUpperCase()匹配。

  • 以下所有内容都很合乎逻辑,并在Java中成立:
  • "ffi".matches(".")拉丁小字体FFI(U + FB03)是字符,因此必须与
  • 匹配
  • "ß".matches(".")拉丁文小写字母SHARP S(U + 00DF)是一个字符,因此必须与
  • 匹配
    Unicode标准的
  • "ffi".toUpperCase().equals("FFI")(没有大写字母FFI)
  • Unicode标准的
  • "ß".toUpperCase().equals("SS")(有大写的S,但没有使用)
  • "FfI".toUpperCase().equals("FFI")显然是
  • "sS".toUpperCase.equals("SS")显然是

  • 因此,假设正则表达式中的第一个点代表,第二个点代表ß,则正则表达式必须匹配“FFISS”,并且由于大小写敏感,因此也匹配“FfIsS”。

    我确实希望有问题,否则正则表达式将变得非常不可用。

    问题:
  • 我的“证明”有什么问题?
  • 如果第二个假设不成立,“不区分大小写”到底是什么意思?
  • 最佳答案

    案例折叠

    答案是否定的,点号不会与大小写匹配,尽管原因有些深奥。

    但是,最了解此类事情的人常常会引起您的困惑,因为他们也觉得这会导致矛盾。

    Unicode中有两种形式的大小写映射。有一种简单的情况映射,其中一个代码点仅映射到另一个代码点。因此,如果使用ss,那么可以保证也使用length(s) == 1,其中length(fc(s)) == 1是Unicode折叠案例图。但它也适用于fcuctc大小写映射。

    这样做的问题是,您无法对某些种类的真实文本进行分析得到满意的结果,然后您将做出精确的1:1长度保证。

    实际上,其中有很多。数字表示在四种情况图中,多少个单独的BMP代码点映射到指定的长度:

    length lc == 2          1
    length lc == 3          0
    
    length fc == 2         88
    length fc == 3         16
    
    length uc == 2         86
    length uc == 3         16
    
    length tc == 2         32
    length tc == 3         16
    

    使用全大写而不是Java的正则表达式使用的简单大写,即使长度不相等,确实也可以使lctschüß之类的东西匹配。在进行不区分大小写的比较时,Perl和Ruby使用完整的大小写映射。如果您不小心的话,这会在否定的字符类中引起奇怪的悖论。

    但是有一个难题:不区分大小写的匹配不会执行传递操作。换句话说,如果TSCHÜSS匹配.且在不区分大小写的情况下匹配,则ß匹配ß,这并不意味着通过传递性SS不区分大小写地匹配.。尽管比我聪明的人对此事已深思熟虑,但这并不是那样的。

    但是,这两个代码点:
  • U + 00DFß拉丁小写字母SHARP S
  • U + 1E9Eẞ拉丁文大写字母S

  • 当然,在全大小写映射下,它们不仅区分大小写,而且匹配SSSSSssS。在简单的案例映射下,他们只是不这样做。

    Unicode确实对此提供了一些保证。一种是,如果是ss,则该length(s) == n,其中length(fn(s)) <= 3*n是以下四个案例图中的任意一个:fnlcfcuc

    归一化

    如果您认为这很糟糕,那么当您考虑归一化形式时,情况实际上会恶化很多。这里的保证是5倍而不是3倍。因此tc,如您所见,它变得越来越昂贵。

    这是一个等效表,显示在四种规范化形式的每种形式下,有多少个代码点扩展为一个以上:

    length NFC  == 2        70
    length NFC  == 3         2
    length NFC  == 4         0
    length NFC  == 5         0
    
    length NFKC == 2       686
    length NFKC == 3       404
    length NFKC == 4        53
    length NFKC == 5        15
    
    length NFD  == 2       762
    length NFD  == 3       220
    length NFD  == 4        36
    length NFD  == 5         0
    
    length NFKD == 2      1345
    length NFKD == 3       642
    length NFKD == 4       109
    length NFKD == 5        16
    

    那不是很了不起吗?一段时间以来,Unicode希望尝试在其模式匹配中建立规范对等。他们知道,由于上述原因,它很昂贵,但是他们花了一段时间才弄清楚,由于在一个字素单元中对字符进行必要的规范重新排序,从根本上来说是不可能的。

    出于这个原因,以及其他许多原因,如果您想“不区分大小写”或“不归一化”地比较事物,当前的建议是自己在两端进行转换,然后比较结果。

    例如,给定一个合适的length(NFx(s)) <= 5 * length(s)码点对码点等价运算符

    fc(a) == fc(b)
    

    同样适用于==模式匹配运算符(当然,该运算符以传统方式工作,不像Java破损的=~方法不适当地固定内容):

    fc(a) =~ fc(b)
    

    这样做的问题是,您不能再在模式的特定部分打开或关闭不区分大小写的功能,例如

    /aaa(?i:xxx)bbb/
    

    并且只区分大小写match部分。

    如Perl和Ruby所证明的那样,完整的 shell 很难,但是可以做到(在大多数情况下)。但这在您应该理解的地方也不太直观(读:令人惊讶)。您必须对带括号的字符类进行特殊处理,尤其是对它们的否定,否则会导致废话。

    语言环境匹配

    最后,要使事情真正复杂,在现实世界中,您需要做的不仅仅是事例映射和规范化中的一个或两个。在某些国家/地区,情况更加复杂。例如,在德语电话簿中,带有变音符的元音与该基本元音后跟字母e的计数完全相同。因此,应该像xxx这样区分大小写地匹配müß

    为了做到这一切,您确实不仅需要绑定(bind)完整的案例映射和规范化表,DUCET本身,Default Unicode Collation Element Table甚至是CLDR数据(请参见引用书目):

    #!/usr/bin/perl
    use utf8;
    use open qw(:utf8 :std);
    use Unicode::Collate::Locale;
    
    my $Collator = Unicode::Collate::Locale->new(
        locale        => "de__phonebook",
        level         => 1,
        normalization => undef,
    );
    
    my $full = "Ich müß Perl studieren.";
    my $sub  = "MUESS";
    if (my ($pos,$len) = $Collator->index($full, $sub)) {
        my $match = substr($full, $pos, $len);
        print "Found match of literal ‹$sub› at position $pos in ‹$full› as ‹$match›\n";
    }
    

    如果运行该命令,则会发现它确实有效:

    Found match of literal ‹MUESS› at position 4 in ‹Ich müß Perl studieren.› as ‹müß›



    精选书目

    这些示例中的大多数都是在其作者的允许下摘自《 Programming Perl》第4版。 :)我在那里写了很多有关此类Unicode问题的文章,这些不是Perl特有的,但总体来说是Unicode的。

    unichars(1)程序,它使我可以收集以下统计信息:

    $ unichars 'length fc == 2' | wc -l
          88
    
    $ unichars 'length NFKD == 4' | wc -l
         109
    
    $ unichars '/ss/i'
    U+00DF ‭ ß  LATIN SMALL LETTER SHARP S
    U+1E9E ‭ ẞ  LATIN CAPITAL LETTER SHARP S
    

    Unicode::Tussle CPAN模块套件的一部分,Brian Foy非常友好地为我维护。

    进一步阅读

    也可以看看:
  • The Unicode Case Folding Table
  • Unicode Standard Annex #15: Unicode Normalization Forms
  • Unicode Technical Standard #18: Unicode Regular Expressions
  • Unicode Technical Standard #10: Unicode Collation Algorithm
  • CLDR - Unicode Common Locale Data Repository
  • 关于java - 假设Unicode和不区分大小写,模式 “..”是否应匹配 “FfIsS”?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/19135354/

    相关文章:

    java - 如何使用带有撇号的 Java 单词边界?

    html - 使用带有 utf 8 的网络字体的 firefox 和 IE 中的特殊字符问题

    php - 如何使用 php 防止 zalgo 文本

    GAE 上的 Java 聊天应用程序将消息写入文件时出现问题

    java - 如何防止hadoop流关闭?

    java - java中的破折号分隔符正在改变输出

    python - 如何从 Pandas 的地址中提取公寓

    java - 通过减去动态配置的时间单位来获取日期的有效方法

    java - 正则表达式:如何提取 <@U34|firstname name|F>

    c++ - unicode char 与非 unicode char 比较,但没有警告或错误