【发布时间】:2015-11-19 05:49:42
【问题描述】:
有没有一种简单的方法可以在 JNI 代码中将 Java 字符串转换为真正的 UTF-8 字节数组?
不幸的是 GetStringUTFChars() 几乎 做了需要但不完全的事情,它返回一个“修改过的”UTF-8 字节序列。主要区别在于修改后的 UTF-8 不包含任何空字符(因此您可以将其视为 ANSI C 空终止字符串),但另一个区别似乎是如何处理 Unicode 补充字符(如表情符号)。
诸如 U+1F604 "SMILING FACE WITH OPEN MOUTH AND SMILING EYES" 之类的字符被存储为代理对(两个 UTF-16 字符 U+D83D U+DE04),并且具有 4 字节 UTF-8 等效项F0 9F 98 84,这就是我在Java中将字符串转换为UTF-8得到的字节序列:
char[] c = Character.toChars(0x1F604);
String s = new String(c);
System.out.println(s);
for (int i=0; i<c.length; ++i)
System.out.println("c["+i+"] = 0x"+Integer.toHexString(c[i]));
byte[] b = s.getBytes("UTF-8");
for (int i=0; i<b.length; ++i)
System.out.println("b["+i+"] = 0x"+Integer.toHexString(b[i] & 0xFF));
上面的代码打印如下:
???? c[0] = 0xd83d c[1] = 0xde04 b[0] = 0xf0 b[1] = 0x9f b[2] = 0x98 b[3] = 0x84
但是,如果我将“s”传递给本机 JNI 方法并调用 GetStringUTFChars(),我会得到 6 个字节。每个代理对字符都被独立地转换为 3 字节序列:
JNIEXPORT void JNICALL Java_EmojiTest_nativeTest(JNIEnv *env, jclass cls, jstring _s)
{
const char* sBytes = env->GetStringUTFChars(_s, NULL);
for (int i=0; sBytes[i]!=0; ++i)
fprintf(stderr, "%d: %02x\n", i, sBytes[i]);
env->ReleaseStringUTFChars(_s, sBytes);
return result;
}
0: 编辑 1:a0 2:BD 3:编 4:b8 5:84
Wikipedia UTF-8 article 表明 GetStringUTFChars() 实际上返回 CESU-8 而不是 UTF-8。这反过来又会导致我的本机 Mac 代码崩溃,因为它不是有效的 UTF-8 序列:
CFStringRef str = CFStringCreateWithCString(NULL, path, kCFStringEncodingUTF8);
CFURLRef url = CFURLCreateWithFileSystemPath(NULL, str, kCFURLPOSIXPathStyle, false);
我想我可以将所有 JNI 方法更改为采用 byte[] 而不是 String 并在 Java 中进行 UTF-8 转换,但这似乎有点难看,有更好的解决方案吗?
【问题讨论】:
标签: java encoding utf-8 java-native-interface