从结尾返回的字符串是否GetStringUTFChars()
以空字符结尾?还是我需要自己确定长度GetStringUTFLength
并使用 null 终止它?
4 回答
是的,GetStringUTFChars
返回一个以 null 结尾的字符串。但是,我认为您不应该相信我的话,而是应该找到一个权威的在线资源来回答这个问题。
让我们从实际的Java 原生接口规范本身开始,它说:
返回一个指向字节数组的指针,该数组表示经过修改的 UTF-8 编码的字符串。这个数组在被释放之前是有效的
ReleaseStringUTFChars()
。
哦,令人惊讶的是,它并没有说明它是否为空终止。男孩,这似乎是一个巨大的疏忽,幸运的是,早在 2008 年,有人就在Sun 的 Java 错误数据库中记录了这个错误。关于错误的注释指出了一个类似但不同的文档错误(没有采取行动就关闭了),建议读者购买一本书《Java 原生接口:程序员指南和规范》,因为有人建议这将成为 JNI 的新规范。
但是我们正在寻找一个权威的在线资源,这既不是权威的(它还不是规范)也不是在线的。
幸运的是,某受欢迎的在线图书零售商对该书的评论表明,该书可从 Sun 免费在线获得,这至少可以满足在线部分的需求。Sun 的JNI 网页有一个链接,看起来非常接近,但遗憾的是,该链接并没有到达它所说的位置。
因此,恐怕我无法为您指出权威的在线资源,您必须购买这本书(实际上是一本好书),它会向您解释:
UTF-8 字符串总是以
'\0'
字符结尾,而 Unicode 字符串不是。要找出以 UTF-8 格式表示 a 需要多少字节jstring
,JNI 程序员可以strlen
对 的结果调用 ANSI C 函数,或者直接对引用GetStringUTFChars
调用 JNI 函数。GetStringUTFLength
jstring
(请注意,在上面的句子中,“Unicode”的意思是“UTF-16”,或者更准确地说是“Java 使用的内部两字节字符串表示,尽管找到证明的方法留给读者作为练习。)
该问题的所有当前答案似乎都已过时(Edward Thomson 的答案上次更新可追溯到 2015 年),或者参考 Android JNI 文档,该文档只能在 Android 世界中具有权威性。最近(2017 年)官方 Oracle JNI 文档清理和更新中已经澄清了这个问题,更具体地说,在这个问题中。
现在JNI规范明确指出:
字符串操作
本规范没有假设 JVM 如何在内部表示 Java 字符串。从这些操作返回的字符串:
- 获取字符串字符()
- GetStringUTFChars()
- 获取字符串区域()
- GetStringUTFRegion()
- GetStringCritical()
因此不需要以 NULL 终止。程序员应通过 GetStringLength() 或 GetStringUTFLength() 确定缓冲区容量要求。
在一般情况下,这意味着永远不要假设 JNI 返回的字符串是空终止的,甚至是 UTF-8 字符串。在务实的世界中,可以在受支持的 JVM 列表中测试特定行为。根据我的经验,参考我实际测试过的 JVM:
- Oracle JVM 会空终止 UTF-16(带
\u0000
)和 UTF-8 字符串(带'\0'
); - Android JVM 会终止 UTF-8 字符串,但不会终止 UTF-16 字符串。
https://developer.android.com/training/articles/perf-jni说:
Java 编程语言使用 UTF-16。为方便起见,JNI 还提供了适用于 Modified UTF-8 的方法。修改后的编码对 C 代码很有用,因为它将 \u0000 编码为 0xc0 0x80 而不是 0x00。这样做的好处是您可以依靠 C 风格的以零结尾的字符串,适合与标准 libc 字符串函数一起使用。不利的一面是您不能将任意 UTF-8 数据传递给 JNI 并期望它正常工作。
如果可能,使用 UTF-16 字符串通常会更快。Android 目前不需要 GetStringChars 中的副本,而 GetStringUTFChars 需要分配并转换为 UTF-8。请注意,UTF-16 字符串不是以零结尾的,并且允许使用 \u0000,因此您需要保留字符串长度以及 jchar 指针。
是的,GetStringUTFChars() 返回的字符串是空终止的。我在我的应用程序中使用它,所以可以通过实验证明它。虽然 Oracle 的文档很烂,但其他来源提供的信息更丰富:Java Native Interface (JNI) Tutorial