移植 Twemoji 正则表达式以提取 Java 中的 Unicode 表情符号

Porting Twemoji regex to extract Unicode emojis in Java

我正在尝试使用 Java 在字符串中识别与 Twemoji 相同的表情符号以供提取。直接端口不适用于大量表情符号 - 我想我已经确定了这个问题,所以我将在下面的示例中给出:

假设我们有表情符号(代码单位为 \ud83e\ude94)。在 Java 脚本正则表达式中,这是由 \ud83e[\ude94-\ude99] 捕获的,它将首先匹配 \ude83e,然后在括号内指示的范围内找到后续的 \ude94。然而,Java 正则表达式中的相同表达式根本无法匹配。如果我根据 online engine 将 Java 模式修改为 [\ud83e[\ude94-\ude99]],则会捕获第二部分,但不会捕获第一部分。

我的工作理论是 Java 遇到括号并将内部的所有内容视为单个代码点,当与外部代码单元结合时,认为它正在寻找两个代码点而不是一个。有没有简单的方法来解决这个问题或正则表达式模式来解决这个问题?明显的解决方法是使用 [\ud83e\ude94-\ud83e\ude99] 之类的东西,实际的正则表达式模式非常冗长。我想知道这里是否也有一个简单的编码修复程序。

玩具样品如下:

public static void main(String[] args) {
    String emojiPattern = "\ud83e[\ude94-\ude99]";
    String raw = "\ud83e\ude94";
    Pattern pattern = Pattern.compile(emojiPattern);
    Matcher matcher = pattern.matcher(raw);
    System.out.println(matcher.matches());
}

如果您要匹配单个特定代码点,请不要乱用代理对;按编号引用它:

String emojiPattern = "\x{1FA94}";

或按姓名:

String emojiPattern = "\N{DIYA LAMP}"

如果要匹配 U+1FA94 所在块中的任何代码点,请使用 属性 原子中的块名称:

String emojiPattern = "\p{blk=Symbols and Pictographs Extended-A}";

如果您切换掉这三个正则表达式中的任何一个,您的示例程序将打印 'true'.

您 运行 遇到的问题是 UTF-16 代理项对是单个代码点,RE 引擎匹配代码点,而不是代码单元;你不能只匹配低半部分或高半部分——只是模式 "\ud83e" 也将无法匹配(当然,当与 Matcher#find 一起使用而不是 Matcher#matches 时)。全部还是none.

要进行您想要的那种范围匹配,您必须远离正则表达式并直接查看代码单元。像

char[] codeUnits = raw.toCharArray();
for (int i = 0; i < codeUnits.length - 1; i++) {
    if (codeUnits[i] == 0xD83E &&
        (codeUnits[i + 1] >= 0xDE94 && codeUnits[i + 1] <= 0xDE99)) {
        System.out.println("match");
    }
}