您刚提到的pdf显示杂项符号和象形文字的范围为1F300-1F5FF。假设我想捕获位于此范围内的任何字符。现在该怎么办呢?
好的,但是我要注意一下,你问题中的表情符号超出了那个范围! :-)
这些字符超过了0xFFFF
,这使事情变得复杂,因为Java字符串存储UTF-16。所以我们不能仅仅使用一个简单的字符类来处理它。我们将会有一对代理项。(更多信息:http://www.unicode.org/faq/utf_bom.html)
在UTF-16中,U+1F300最终成为一对\uD83C\uDF00
;U+1F5FF最终成为\uD83D\uDDFF
。请注意,第一个字符上涨了,我们越过了至少一个边界。因此,我们必须知道我们正在寻找哪些代理项范围。
由于我不熟悉UTF-16的内部工作原理,因此我编写了一个程序来进行探索(源代码在最后 - 如果我是您的话,我会反复检查一遍,而不是相信我)。它告诉我,我们正在寻找\uD83C
后跟范围\uDF00-\uDFFF
(包括这两个字符)中的任何东西,或\uD83D
后跟范围\uDC00-\uDDFF
(包括这两个字符)中的任何东西。
因此,理论上,我们现在可以编写一个模式:
// This is wrong, keep reading
Pattern p = Pattern.compile("(?:\uD83C[\uDF00-\uDFFF])|(?:\uD83D[\uDC00-\uDDFF])")
这是两个非捕获组的交替,第一组用于以 \uD83C
开头的情况,第二组用于以 \uD83D
开头的情况。
但是匹配失败(没有找到任何内容)。我相当确定这是因为我们尝试在各种地方指定了一个代理对中的一半:
Pattern p = Pattern.compile("(?:\uD83C[\uDF00-\uDFFF])|(?:\uD83D[\uDC00-\uDDFF])");
// Half of a pair --------------^------^------^-----------^------^------^
我们不能简单地将代理对拆分开来,它们被称为代理对是有原因的。 :-)
因此,我认为我们无法使用正则表达式(或任何基于字符串的方法)来解决这个问题。 我认为我们必须通过搜索char
数组来解决。
char
数组包含UTF-16值,因此如果我们用艰难的方式查找,就可以找到这些半对。
String s = new StringBuilder()
.append("Thats a nice joke ")
.appendCodePoint(0x1F606)
.appendCodePoint(0x1F606)
.appendCodePoint(0x1F606)
.append(" ")
.appendCodePoint(0x1F61B)
.toString();
char[] chars = s.toCharArray();
int index;
char ch1;
char ch2;
index = 0;
while (index < chars.length - 1) {
ch1 = chars[index];
if ((int)ch1 == 0xD83C) {
ch2 = chars[index+1];
if ((int)ch2 >= 0xDF00 && (int)ch2 <= 0xDFFF) {
System.out.println("Found emoji at index " + index);
index += 2;
continue;
}
}
else if ((int)ch1 == 0xD83D) {
ch2 = chars[index+1];
if ((int)ch2 >= 0xDC00 && (int)ch2 <= 0xDDFF) {
System.out.println("Found emoji at index " + index);
index += 2;
continue;
}
}
++index;
}
显然那只是调试级别的代码,但它完成了工作。(在您提供的带有表情符号的字符串中,当然不会找到任何内容,因为它们超出了范围。但是如果您将第二对的上限更改为0xDEFF
而不是0xDDFF
,那么就可以找到了。不知道这是否还包括非表情符号。)
我编写程序以查找代理范围的来源:
public class FindRanges {
public static void main(String[] args) {
char last0 = '\0';
char last1 = '\0';
for (int x = 0x1F300; x <= 0x1F5FF; ++x) {
char[] chars = new StringBuilder().appendCodePoint(x).toString().toCharArray();
if (chars[0] != last0) {
if (last0 != '\0') {
System.out.println("-\\u" + Integer.toHexString((int)last1).toUpperCase());
}
System.out.print("\\u" + Integer.toHexString((int)chars[0]).toUpperCase() + " \\u" + Integer.toHexString((int)chars[1]).toUpperCase());
last0 = chars[0];
}
last1 = chars[1];
}
if (last0 != '\0') {
System.out.println("-\\u" + Integer.toHexString((int)last1).toUpperCase());
}
}
}
输出:
\uD83C \uDF00-\uDFFF
\uD83D \uDC00-\uDDFF
<U+1F606>
字符串是特定于less
的 - 此外,您的解决方案想法也会捕获几乎任何其他Unicode字符。唯一真正的解决方案是拥有所有与表情符号相对应的Unicode代码点列表。 - Drew McGowen