Miscellaneous Symbols and Pictographs是一个Unicode块,其中包含气象和天文符号,表情符号字符(主要是与日本电话运营商的Shift JIS实现兼容)以及最初来自Microsoft Windows中的Wingdings和Webdings字体的字符。
所引用的维基百科文章指定的Unicode范围是U+1F300..U+1F5FF
但是,如果我从列表中选择一个表情符号并进行正则表达式匹配,它将失败。
var a = "🌍";
var matched = a.match(/[\u1F300-\u1F5FF]/);
matched
始终为null。这是为什么?我在哪里出错? 最佳答案
问题
Java已有Unicode Problem一段时间了。超出U + 0000 ... U + FFFF范围的Unicode代码点被称为星体代码点,并且存在问题,因为它们不易通过正则表达式进行匹配:
// `🌍` is an astral symbol because its codepoint value
// of U+1F30D is outside the range U+0000...U+FFFF
// Astral symbols do not work with regular expressions as expected
var regex = /^[bc🌍]$/;
console.log(
regex.test('a'), // false
regex.test('b'), // true
regex.test('c'), // true
regex.test('🌍') // false (!)
);
console.log('🌍'.match(regex)); // null (!)
原因是因为此一个星体代码点实际上是由两部分组成,或更确切地说,由两个“代码单元”组成,并且这两个代码单元结合在一起形成了字符。
console.log("\u1F30D") // Doesn't work
console.log("\uD83C\uDF0D") // 🌍
星号🌍实际上由两个代码单元组成:🌍= U + D83C + U + DF0D!
因此,如果要匹配此星号,则必须使用以下正则表达式和匹配器:
var regex = /^([bc]|\uD83C\uDF0D)$/;
console.log(
regex.test('a'), // false
regex.test('b'), // true
regex.test('c'), // true
regex.test('\uD83C\uDF0D') // true
);
console.log('\uD83C\uDF0D'.match(regex)); // { 0: "🌍", 1: "🌍", index: 0 ... }
所有星体符号都有此分解。惊讶吗好吧,也许你应该-这种情况很少发生!它仅在很少使用的星体代码点发生。我本人和全世界其他人使用的大多数代码点都不是星形的-它们在U + 0000 ... U + FFFF的范围内,因此我们通常不会看到此问题。表情符号是此规则的新例外-所有表情符号都是星号符号,并且由于社交媒体的使用,它们在全球范围内的使用日益广泛。
不幸的是,使用这样的代码单元是Unicode的实现细节,而Java程序员对此却很陌生。由于尚不清楚是使用字符逐字(🌍)还是使用代码单元分解(U + D83C + U + DF0D),无论何时使用诸如
match
,test
,。之类的字符串函数,它都容易引起程序员的困惑。 ..被使用;或每当使用正则表达式和字符串文字时。但是语言设计者和实现者正在努力改进。解决方案
ECMAScript 6(ES6)的最新功能是对正则表达式匹配的introduction of a
u
flag。这使您可以按代码点进行匹配,而不是按代码单位进行匹配(默认)。var regex = /^[bc🌍]$/u; // <-- u flag added
console.log(
regex.test('a'), // false
regex.test('b'), // true
regex.test('c'), // true
regex.test('🌍') // true <-- it now works!
);
通过使用
u
标志,您不必担心代码点是否为星体代码点,也不必在代码单元之间来回转换。 u
标志使正则表达式以直观的方式工作-即使是表情符号!但是,并非每个版本的Node.js以及每个浏览器都支持此新功能。为了支持所有环境,您可以使用regenerate之类的库。