JNI中jchararray转jstring问题:Linux下仅返回首个字符
问题根源分析
你遇到的问题核心是jchar与char的编码和字节长度不匹配:
- JNI里的
jchar对应Java的char,是2字节的UTF-16编码字符 - 你用单字节的
char*数组来存储,当GetCharArrayRegion把2字节的jchar写入单字节数组时,ASCII范围内字符的高字节(通常是0)会被当成C字符串的终止符\0,导致NewStringUTF读取到第一个0就停止解析,最终只返回第一个字符。
正确的实现方案
下面提供两种针对性的解决办法,你可以根据实际需求选择:
方案1:直接用JNI函数转成jstring(最简单高效)
如果你的需求只是把jchararray转换成Java的String,完全不需要转成C的char数组,直接用NewString一步到位:
jstring JNICALL Java_YourClass_YourMethod(JNIEnv *env, jobject obj, jcharArray ch) { jsize length = (*env)->GetArrayLength(env, ch); // 获取jchar数组的指针 jchar *jchars = (*env)->GetCharArrayElements(env, ch, NULL); if (jchars == NULL) { return NULL; // 处理内存分配失败的情况 } // 直接用UTF-16的jchar数组创建jstring jstring result = (*env)->NewString(env, jchars, length); // 释放JNI资源,JNI_ABORT表示不需要把修改回写到原数组 (*env)->ReleaseCharArrayElements(env, ch, jchars, JNI_ABORT); return result; }
方案2:转成UTF-8的C字符串(适合C端需要处理字符串的场景)
如果你确实需要先得到C端的UTF-8字符串,得做编码转换,这里用iconv库实现(编译时记得加-liconv链接参数):
#include <stdlib.h> #include <string.h> #include <iconv.h> jstring JNICALL Java_YourClass_YourMethod(JNIEnv *env, jobject obj, jcharArray ch) { jsize length = (*env)->GetArrayLength(env, ch); jchar *jchars = (*env)->GetCharArrayElements(env, ch, NULL); if (jchars == NULL) { return NULL; } // 预估UTF-8最大所需空间:每个UTF-16字符最多转成3字节UTF-8,加1个终止符 size_t utf8_buf_len = length * 3 + 1; char *utf8_str = (char*)malloc(utf8_buf_len); if (utf8_str == NULL) { (*env)->ReleaseCharArrayElements(env, ch, jchars, JNI_ABORT); return NULL; } // 打开UTF-16到UTF-8的转换句柄,Java用的是大端UTF-16,对应"UTF-16BE" iconv_t conv_handle = iconv_open("UTF-8", "UTF-16BE"); if (conv_handle == (iconv_t)-1) { free(utf8_str); (*env)->ReleaseCharArrayElements(env, ch, jchars, JNI_ABORT); return NULL; } // 执行编码转换 char *in_ptr = (char*)jchars; size_t in_remaining = length * 2; // jchar是2字节,总输入字节数是length*2 char *out_ptr = utf8_str; size_t out_remaining = utf8_buf_len - 1; if (iconv(conv_handle, &in_ptr, &in_remaining, &out_ptr, &out_remaining) == (size_t)-1) { iconv_close(conv_handle); free(utf8_str); (*env)->ReleaseCharArrayElements(env, ch, jchars, JNI_ABORT); return NULL; } *out_ptr = '\0'; // 给UTF-8字符串加终止符 // 创建最终的jstring jstring result = (*env)->NewStringUTF(env, utf8_str); // 释放所有资源 iconv_close(conv_handle); free(utf8_str); (*env)->ReleaseCharArrayElements(env, ch, jchars, JNI_ABORT); return result; }
再聊下你的原代码问题
你的原代码里,GetCharArrayRegion是把jchar类型的元素复制到目标数组,但你传入的是char*数组,每个jchar会被拆成两个单字节写入。比如字符'a'的UTF-16编码是0x0061,写入后carr[0]是0x61(即'a'),carr[1]是0x00(即字符串终止符),NewStringUTF读到这里就认为字符串结束了,自然只返回第一个字符。
内容的提问来源于stack exchange,提问作者Anil kumar
相关产品推荐
相关产品推荐

