JNI:将Java字符串转换为代码页面1252

时间:2012-03-29 18:09:27

标签: java c++ unicode java-native-interface

我正在使用JNI来连接Java程序和C ++函数。 C ++函数处理多字节字符串(CP 1252)。我使用这个C ++代码将Java String转换为char *:

char *arg=(char*) jEnv->GetStringUTFChars(jArg2,0);

除非我有一些高阶字符,否则这样可以正常工作。例如,如果我的输入是:

Àlan(UTF:c2 6c 61 6e 20 4a 6f 6e 65 7e)

我可以看到结果arg是:

c3 82 6c 61 6e

但是,我希望看到:

c0 6c 61 6e

看到GetStringUTFChars()应该返回UTF字符串,我尝试使用GetStringChars()获取Unicode字符串并通过WideCharToMultiByte()转换它:

const jchar *str=jEnv->GetStringChars(jArg2,0);
WideCharToMultiByte(CP_UTF8,0,(LPCWSTR) str,jEnv->GetStringLength(jArg2),str,szStr,0,0);

(你可以假设我已经分配了str并正确设置了szStr)。在这种情况下,我在结果str中看到了这一点:

c3 82 6c 61 6e

我已经为WideCharToMultiByte的第一个参数尝试了其他CP_值,没有产生有用的结果(它们要么返回上面的内容,要么用“?”代替'À'。

我希望不知怎的,我可以得到这个结果str:

c0 6c 61 6e

但到目前为止,我没有运气。

2 个答案:

答案 0 :(得分:3)

Java使用修改的版本的UTF-8。以下是Java文档的引用:

  

修改后的UTF-8对Java平台来说并不陌生,但它确实如此   应用程序开发人员需要在转换时更加了解   可能包含与UTF-8相关的补充字符的文本。   要记住的主要是一些J2SE接口使用   编码类似于UTF-8但与之不兼容。这个   编码过去有时被称为“Java modified UTF-8”   或(错误地)只是“UTF-8”。对于J2SE 5.0,文档是   被更新以统一称其为“修改过的UTF-8。”

     

修改后的UTF-8与标准UTF-8之间的不兼容性   从两个不同。首先,修改后的UTF-8代表角色   U + 0000为双字节序列0xC0 0x80,而标准UTF-8使用   单字节值0x0。其次,修改后的UTF-8代表   通过分别编码两个代理代码来补充字符   他们的UTF-16表示单位。每个代理代码单元   由三个字节表示,总共六个字节。标准   另一方面,UTF-8使用单个四字节序列   完整的角色。

     

Java虚拟机和接口使用修改的UTF-8   附加到它(例如Java Native Interface,各种工具   java.io.DataInput和中的接口或Java类文件)   DataOutput接口和实现或使用它们的类,以及用于   序列化。 Java Native Interface提供了这些例程   转换为修改后的UTF-8。另一方面,标准UTF-8   由String类支持,由java.io.InputStreamReader和   OutputStreamWriter类,java.nio.charset工具等等   API层叠在它们之上。

     

由于修改后的UTF-8与标准UTF-8不兼容,因此它是   至关重要,不要在需要另一个的地方使用。修改后的UTF-8可以   仅用于上述Java接口。在所有其他   情况,特别是对于可能来自或可能来自的数据流   由不基于Java平台的软件解释,   必须使用标准UTF-8。 Java Native Interface例程   当标准UTF-8时,不能使用转换为修改后的UTF-8   是必需的。

字节序列c2 6c 61 6e 20 4a 6f 6e 65 7e在标准UTF-8下无效。在cp1252中,相同的字节序列是字符串Âlan Jone~(注意Â而不是À)。

在标准UTF-8下,字符串Àlan Jone~将是字节序列c3 80 6c 61 6e 20 4a 6f 6e 65 7e(注意c3 80 6c而不是c2 6c)。

所有Java字符串本身都是UTF-16,因此您无需将字符串检索为UTF-8。使用GetStringChars()获取原始UTF-16编码字符并按原样将其传递给WideCharToMultiByte(),指定1252作为代码页(请注意,在您的示例中使用的是str对于UTF-16输入缓冲区和cp1252输出缓冲区 - 不要让你的变量混淆!),例如:

const jchar *str = jEnv->GetStringChars(jArg2,0); 
char *cp1252 = NULL;
int len = WideCharToMultiByte(1252, 0, (LPCWSTR)str, jEnv->GetStringLength(jArg2), NULL, 0, 0, 0);
if (len > 0)
{
    cp1252 = new char[len + 1];
    WideCharToMultiByte(1252, 0, (LPCWSTR)str, jEnv->GetStringLength(jArg2), cp1252, len, 0, 0); 
    cp1252[len] = 0;
}

答案 1 :(得分:0)

Code ANSI 1252,Windows ANSI Western,是ISO Latin 1的超集。它是Unicode的子集。因此,如果您可以在没有欧元符号和其他一些添加的Microsoft字符的情况下生存,只需丢弃任何高于255的Unicode代码点,并且您有一个有效的cp 1252编码字符串。

为了正确使用WideCharToMultiByte(更一般的转换,例如支持欧元符号),阅读文档,并注意例如旗帜值。

或者正如我们以前在Usenet上所说的那些希望其他人为他们阅读文档并告诉他们重要内容和不重要内容的人,请转发RTFM。