java - 移植 Twemoji regex 以在 Java 中提取 Unicode emojis

我正在尝试使用 Java 在字符串中识别 https://twemoji.maxcdn.com/v/latest/twemoji.js 提取的相同 emojis。直接端口不适用于大量 emojis - 我想我已经确定了这个问题,所以我将在下面的示例中给出:

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

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

玩具样品如下:

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());
}

回答1

如果您正在尝试匹配单个特定代码点,请不要混淆代理对;按编号引用它:

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 一起使用时)。全有或全无。

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

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");
    }
}

相似文章