标签:资源 color 显示 poi 字符编码 info head seq 系统
通常用户自行修改资料是很常见的需求,我们规定昵称长度在2到10之间。假设用户试图使用表情符号 ????????????
作为用户名,请求是否合法?
打开浏览器控制台,输入 ‘???????????‘.length
,打印结果是11。
公司项目涉及内容打印的,之前将 Emoji 显示成乱码、框框是家常便饭,而且手机和浏览器、打印物各种不一致也相当折磨人。硬头皮阅读 unicode.org/emoji ,使用哈希查找暂解决了问题。
年前项目遇到敏感词过滤的需求,各种参考,结合之前的 Emoji 方案,方才有桃花源 “复行数十步,豁然开朗”的感悟,解决方案得到了升级。
以下内容是关于字典树-TrieTree的初级使用,并运用到 Emoji 定位查找和敏感词过滤的实际过程。
对于我们程序员,Emoji 带来了诸多问题
解决这些问题不可能脱离 Unicode 字符来谈。
由于 JavaScript 只能处理 UCS-2 编码,造成所有字符在这门语言中都是2个字节,如果是4个字节的字符,会当作两个双字节的字符处理。JavaScript 的字符函数都受到这一点的影响,无法返回正确结果。
在阅读完以上资料后,想必前面的两个问题有了初步概念。以下是 Unicode 字符"??"在部分编程语言及版本中的体现。
编程语言 | 字符集 | 编码 | 字符"??"的字面量 |
---|---|---|---|
C# | Unicode | UTF-16 | "\ud834\udf06" |
Java | Unicode | UTF-16 | "\ud834\udf06" |
ECMAScript 5 | Unicode | UCS-2 | "\ud834\udf06" |
ECMAScript 6 | Unicode | UCS-2, UTF-16 | "\ud834\udf06", "\u{1d306}" |
Python ? | ? | ? | u‘\U0001d306‘ |
概括来说,UTF-16使用一组规则扩充了字符集。
- 如果字符编码U小于0x10000,也就是十进制的0到65535之内,则直接使用两字节表示;
- 如果字符编码U大于0x10000,由于UNICODE编码范围最大为0x10FFFF,从0x10000到0x10FFFF之间 共有0xFFFFF个编码,也就是需要20个bit就可以标示这些编码。用U‘表示从0-0xFFFFF之间的值,将其前 10 bit作为高位和16 bit的数值0xD800进行 逻辑or 操作,将后10 bit作为低位和0xDC00做 逻辑or 操作,这样组成的 4个byte就构成了U的编码。
Java
String str = "\ud834\udf06";
System.out.printf("str: %s, length: %d", str, str.length());
// str: ??, length: 2
C#
mString str = "\ud834\udf06";
Console.WriteLine("str: {0}, length: {1}", str, str.Length);
// str: ??, length: 2
JavaScript
> let str = "\ud834\udf06";
> str
< "??"
> console.log("str: %s, length: %d", str, str.length);
str: ??, length: 2
Python 3
>>> s = "\ud834\udf06"
>>> s
'\ud834\udf06'
>>> len(s)
2
Python 2
>>> s = "\ud834\udf06"
>>> s
'\\ud834\\udf06'
>>> len(s)
12
>>> s = u'\ud834\udf06'
>>> s
u'\U0001d306'
>>> len(s)
2
多数的编程语言的"字符串长度"表达的是"字符串占用字节的长度"。可视字符的长度计算和检索需要先将字节序列转化为 Unicode 字符序列。采用UTF-16 的编程语言有能力能够理解上述规则,但由于历史问题等基于 UCS-2 的 ECMAScript 5 及 Python2 就悲剧了。
C# Char.IsHighSurrogate
和 StringInfo
//获取 unicode 码点
public static IEnumerable<Int32> CodePoints(this String s) {
for (int i = 0; i < s.Length; ++i) {
yield return Char.ConvertToUtf32(s, i);
if (Char.IsHighSurrogate(s, i))
i++;
}
}
public static IEnumerable<String> TextElements(String s) {
var enumerator = StringInfo.GetTextElementEnumerator(s);
while (enumerator.MoveNext()) {
yield return enumerator.GetTextElement();
}
}
ECMAScript 6 String.prototype.codePointAt(index: number)
需要注意的是,对于4字节码点字符,如果参数大于Unicode字符数时,String.prototype.codePointAt
函数仍然生效但退化成了 String.prototype.charCodeAt
的实现。
故不能简单实现成 let codePoints = s => Array.from([...s].keys()).map(i => s.codePointAt(i));
let s = '????????';
let codePoints = s => Array.from([...s].keys()).map(i => s.codePointAt(i));
codePoints(s)
//[128104, 56424, 8205, 128105, 56425] ERROR!!!
正确的做法
let s = '????????';
let codePoints = s => [...s].length === 1
? Array.from([...s].keys()).map(i => s.codePointAt(i))
: Array.prototype.concat.call(...[...s].map(codePoints));
codePoints(s)
//(5) [128104, 8205, 128105, 8205, 128102]
Emoji 最早在日本兴起,然后由 Apple 引入,目前是国际标准,见于Unicode? Emoji 。这个过程带来了各种历史遗留问题(后边会提提及),而Emoji 本身也在持续发展中,今天的资料可能变成明日黄花。
有了对 Unicode 的科普在前,我们现在知道 Emoji 只是 Unicode 字符或序列,文本渲染引擎遇到它们时进行解析和替换成自有实现。
略微提及,macOS 和 Android 分别使用的解决方案关键字是 AppleColorEmoji 和 NotoColorEmoji`,涉及TTF字休编程等,如有需要请自行搜索。
由此可见,Emoji 长度虽然确定但不能目测;如何显示是文本渲染引擎的工作,但不同的平台、 浏览器、 厂商甚至各个版本之间都有巨大的差异。
探究 emoji 字符长度 有一段代码演示了 Emoji 字符长度的表现。
// neutral family
// U+1F46A
// length: 2
> ??
// ZWJ sequence: family (man, woman, boy)
// U+1F468 + U+200D + U+1F469 + U+200D + U+1F466
// ??? + U+200D + ??? + U+200D + ??
// length: 8
> ?????????
// ZWJ sequence: family (woman, woman, girl)
// U+1F469 + U+200D + U+1F469 + U+200D + U+1F467
// ??? + U+200D + ??? U+200D + ??
// length: 8
> ?????????
// ZWJ sequence: family (woman, woman, girl, girl)
// U+1F469 + U+200D + U+1F469 + U+200D + U+1F467 + U+200D + U+1F467
// ??? + U+200D + ??? + U+200D + ??? + U+200D + ??
// length: 11
> ????????????
这段文本可能因为浏览器版本等原因看到表情序列而不是组合,故我在 Chrome 下对显示效果作了截图
Twitter 对 Emoji 跨平台一致显示的解决方案在 twitter/twemoji。它有以下问题:
我们想知道一段文本里边有什么 Emoji,在哪里,怎样替换,怎样自定义显示,需要更多的掌控。
初级字典树查找在 Emoji、关键字检索上的运用 Part-1
标签:资源 color 显示 poi 字符编码 info head seq 系统
原文地址:https://www.cnblogs.com/Jusfr/p/9559635.html