likes
comments
collection
share

Emojis, Java and Strings

作者站长头像
站长
· 阅读数 8

原作者:Daniel Lemire

原文地址:lemire.me/blog/2018/0…

emoji 是有趣的字符,并且越来越流行。但是,对于程序员来说,处理 emoji 可能并不像你想象的那样简单。为了对比,我们通过 Python3 来使用 emoji。代码如下,我们定义了一个包括表情符号的字符串,然后访问索引 1 的字符(第二个字符)。

>>> x= "😂😍🎉👍"
>> len(x)
4
>>> x[1]
'😍'
>>> x[2]
'🎉'

可以看出,这样做的效果很好。然而,这在 Python2 中失败了。所以请升级到 Python3 (Python3十年前就出来了)。

那么JavaJavaScript的效果会怎么样呢?它们两个类似,这里将专注于Java。在java 中,你可以很很简单地定义 emoji 的字符串。

String emostring = "😂😍🎉👍"

然而,这只是麻烦的开始。如果你试图获取字符串的长度(emostring.length()),Java 会告诉你这个字符串包含 8 个字符。如果要在 "unicode代码点" 中获得字符串的正确长度,你需要使用 emostring.codePointCount(0, emostring.length()),该方法会返回4。这不仅耗时较长,而且我预计该方法的计算成本也会高得多。

那么访问字符呢?你可能认为 emostring.charAt(1) 会返回第二个字符(😍),但是它失败了。因为Java使用UTF-16编码,这意味着,一个unicode字符可以使用一个16位或两个16位来表示,这取决于字符的情况。因此,如果给你一串字符串的字节流,你不扫描它就无法知道这个字符串有多长。同时,Java中的字符类型(char)是16位的,所以它不能表示所有的 unicode字符。例如,你不能用Java的char来表示一个表情符号。在Java中,为了得到第二个字符,你需要做一些麻烦的操作,比如说:

new StringBuilder().appendCodePoint(
emostring.codePointAt(emostring.offsetByCodePoints(0, 1)).toString()

我相信你可以找到更短的方法,但这是它的要点。而且,它比 charAt 要耗时得多。

如果你的应用程序需要随机访问一个长字符串中的 unicode字符,你就有可能出现性能问题。 其他语言的实现,比如 PyPy(PyPy是用Python实现的Python解释器的动态编译器) 使用 UTF-32 编码,与 JavaUTF-16 编码不同,它支持对单个字符的快速随机访问。缺点是内存占用增加。事实上,PyPy 似乎想转向UTF-8,这是目前网络上的主流格式。在UTF-8中,字符用1、2、3或4个字节表示。

这里面有不同的权衡。如果内存不是问题,而且你希望使用表情符号,并且能在长字符串中快速随机访问,我认为UTF-32更有优势;如果你想要类似UTF-8这样的格式,来获得一些良好的性能,同时对长字符串有某种形式的索引,如果你使用的语言不允许你直接访问底层实现,这可能很难实现。

比性能更烦人的是对程序员的纯粹的不方便。现在是2018年(文章发布时间是2018年,译者注)。访问一串表情符号中的第二个表情符号需要几乎无法解读的代码,这实在令人无法接受。

附录:是的。我知道不同的代码点可以组合成一个可见的字符,所以我把 "字符 "和 "代码点 "等同起来,为了简化问题。我也知道,不同的码点序列可以产生相同的字符。但这只会使目前的编程状态更糟。