JNI 开发中字符编码的一点坑
Jul 23, 2016
坑
JNI 开发中 Java 层向下传字符串比较常用的是 JNIEnv 的 GetStringUTFChars 方法将 jstring
转为 const char *
,用完后使用 ReleaseStringUTFChars 方法释放。
1 | const jchar * GetStringChars(JNIEnv *env, jstring string, |
然而使用该方法返回的字符串却并非采用标准 UTF-8 编码,而是Modified UTF-8 Strings,即一种修改过的 UTF-8 编码。
标准的 UTF-8 编码以 8-bit 即一个字节为基本单位,一个字符可以由一到六个字节编码表示,只要留出每个字节的高几位作为标志位就可以表示出该字节的类型,这样就可以判断出其后有几个字节与该字节合在一起表示一个字符。比如最高位为 0
表示该字节单独表示一个字符;最高两位为 10
表示该字节是跟在其他字节后面的,仅包含数据;最高三位为 110
则表示它将与后一个字节共同表示同一字符。
在 Modified UTF-8 Strings 中,U+FFFF
以上编码的字符(比如 Emoji 字符)并没有继续遵循 UTF-8 编码的规则,而是将其拆分为两个部分,分别使用三个字节存放,共六个字节。但这样也就导致了使用 UTF-8 编码解析的话便将会其识别为两个字符。
1 | 1 | 1 | 0 | 1 | 1 | 0 | 1 |
1 | 0 | 1 | 0 | ||||
1 | 0 | ||||||
1 | 1 | 1 | 0 | 1 | 1 | 0 | 1 |
1 | 0 | 1 | 1 | ||||
1 | 0 |
解决方案
主要有的解决思路有两种,一种是使用 UTF-16 编码,系统提供了对应的 GetStringChars 和 ReleaseStringChars 方法。
1 | const jchar * GetStringChars(JNIEnv *env, jstring string, |
另一种是先在 Java 层拿到 UTF-8 编码的字符串 byte[] 数据,再以 jbyteArray 的形式传入。
1 | byte[] data = str.getBytes("UTF-8"); |
1 | // jbyteArray _bytes; |