本站首页    管理页面    写新日志    退出


«October 2019»
12345
6789101112
13141516171819
20212223242526
2728293031


公告

戒除浮躁,读好书,交益友


我的分类(专题)

日志更新

最新评论

留言板

链接

Blog信息
blog名称:邢红瑞的blog
日志总数:523
评论数量:1142
留言数量:0
访问次数:9313980
建立时间:2004年12月20日




[jvm]linux下的jni中文问题
原创空间,  软件技术,  电脑与网络

邢红瑞 发表于 2005-10-14 11:31:57

网站的用户认证使用RSA加密方式,架构师认为使用JCA需要耗费CPU的资源,要使用Openssl,我想了一下使用c就不消耗cpu资源了,所以准备使用jni。服务器使用英文版linux,因为linux命令是英文,而且在远端vi中,看到的中文页是乱码,所以我们安装服务器就是安装英文linux,没想到这也是个问题。因为用户名是中文,java传递一些中文字串到 C,但是c是乱码JNIEXPORT jint JNICALL Java_test_string  (JNIEnv *env, jobject jobj, jstring name, jint no){  jboolean iscopy;  int length;      const char *temp = (*env)->GetStringUTFChars(env, name, &iscopy);  printf("temp is %s\n",temp);  length = no * 2;  return length;}后来发现GetStringUTFChars 传回來的是字串以 Java modified UTF-8 编码的 byte array(一ge unicode char 是 2 bytes,以 UTF-8 是1到3个bytes 不等),用 printf一个 byte一个 byte打出来就是乱码。 应该用底层平台提供处理 unicode 的函数來作输出,因为printf只用于ascii码,只好使用String 的 getBytes 方法,指定平台使用的中文的编码GBK。 JNIEXPORTjint JNICALL Java_test_string(JNIEnv *env, jobject jobj,  jstring msg, jint no){  jclass klass = env->GetObjectClass(msg);  jmethodID getBytes = env->GetMethodID(klass, "getBytes", "(Ljava/lang/String;)[B");  jvalue arg;  arg.l = env->NewStringUTF("GBK");  jbyteArray chs = (jbyteArray) env->CallObjectMethodA(msg, getBytes, &arg);  int len = env->GetArrayLength(chs);  jbyte* msgP = env->GetByteArrayElements(chs, 0);  char* buf = new char[len + 1];  buf[len] = '\0';  for (int i = 0; i < len; ++i)    buf[i] = msgP[i];  env->ReleaseByteArrayElements(chs, msgP, JNI_ABORT);  printf("Message is: %s\n", buf);  delete[] buf;  return no * 2;} jmethodID getBytes = env->GetMethodID(klass, "getBytes", "(Ljava/lang/String;)[B");是取得 method: {byte[] getBytes(String)} 的 method id。c的代码JNIEXPORT jint JNICALL Java_testString(JNIEnv *jenv, jclass jcls, jstring jarg1) {    jclass klass;    jmethodID getByteID;    jbyteArray chs=0;    jvalue arg;    int len, i;    char buf[128];    jbyte *msgP;     klass = (*jenv)->GetObjectClass(jenv, jarg1);    getByteID = (*jenv)->GetMethodID(jenv, klass, "getBytes",                                                         "(Ljava/lang/String;)[B");    arg.l = (*jenv)->NewStringUTF(jenv, "GBK");    chs = (jbyteArray)(*jenv)->CallObjectMethodA(jenv, jarg1,                                                                     getByteID, &arg);    len = (*jenv)->GetArrayLength(jenv, chs);    printf("len=%d\n",len);    msgP = (*jenv)->GetByteArrayElements(jenv, chs, 0);    for ( i = 0; i < len; ++i)        buf[i] = msgP[i];    buf[len]= '\0';    (*jenv)->ReleaseByteArrayElements(jenv, chs, msgP, JNI_ABORT);    printf("Message is: %s\n", buf);    return 1;}发现jni还是乱码,只好 把byte array一个一个打印出来和UltraEdit比较,记得或是你使用在 windows 平台 , MessageBox 显示buf ,可以看到中文,因为 VC,使用 character set 为MBCS(Muilti-Byte Character Set)。后来发现编译 Java source 使用 ISO8859-1 来编译,所以编译是 source 中的string的 literal以 ISO8859-1 的编码抓换成 unicode,,如果unicode 字串再以 GBK转为成 byte array 就不是真正的GBK的 byte array。 也可以可以用 JDK 的native2ascii来转码,最好看一下 file.encoding 是那个编码。 如果编译时没有有指定编码方式,编译器会以系统设定编码 source code,英文linux是 ISO8859-1, 在编译-encoding GBK指定编码。又发现jni是中文,但是java又成了乱码,看了看文档,明白了javac -encoding GBK *.java编译 必须用 java -Dfile.encoding=GBK 执行 因为output steam 的 charset 默认把非 ISO 的GBK字符都转为 ? 才 print 出来。但是发现还是不行,因为使用-encoding GBK编译,但是System.getProperty("file.encoding")还是ISO-8859-1,只好设linux了,export LANG=zh_CN,这下不用-encoding GBK,居然也是中文。在Java系统中使用 PrintStream 打印,如果 PrintStream没有指定 encoding 就是使用OS设定的 encoding例如System.out 。PrintStream 在输出字符串是就是指定 PrintStream 使用的 encoding ,使用 String 的 getBytes方法得到一个 byte array,再把 byte array element 输出,所以 unicode String 的用户名 以 ISO-8859-1 encoding 解码时,大于 127 的 byte 变成 '?'。可以强制使用try{       System.setOut( new PrintStream(System.out, true, "GBK"));}catch(java.io.UnsupportedEncodingException e){      }finally{   }关于c++把中文返回java的写法C语言中的字符串不是 UTF-8而是,而是 mbcs。 可以使用 C++ wchar_t ,或是事先用UltraEdit把中文字符转成 UTF8 格式。  wchar_t* p = L"邢红瑞"; //  unicode  return env->NewString(reinterpret_cast<jchar*>(p), wcslen(p));c中没有casting 这样写(*env)->NewString(env, (jchar*) p, wcslen(p));发现jni调用java的类却是麻烦,看了看NLS strings规范使用 Windows本地方法有两个 JNI APIs 访问Java strings使用 Windows本地方法GetStringUTFCharsGetStringCharswindows的例子import java.awt.*;import java.awt.event.*;public class StringTest extends Frame implements ActionListener{  private TextField input = new TextField("",20);  private Button sendButton = new Button( "Send" );  public StringTest()  {    super("String Test");    setLayout( new FlowLayout() );    add( input );    add( sendButton );    sendButton.addActionListener( this );  }  public void actionPerformed( ActionEvent evt )  {    if( evt.getSource()==sendButton )    {      String s = input.getText();      showParms( s, s );    }  }  public void showParms( String s1, String s2 )  {    showParms0( s1, s2 );  }  public native void showParms0( String s1, String s2 );  static  {    System.loadLibrary( "StringTest" );  }  public static void main( String args[] )  {    StringTest frame = new StringTest();    frame.addWindowListener(new WindowAdapter() {      public void windowClosing(WindowEvent e) {System.exit(0);}    });    frame.setSize(180, 200);    frame.setVisible(true);  }}#include <windows.h>#include <stdio.h>#include "StringTest.h"char* jstringToWindows( JNIEnv* env, jstring jstr );JNIEXPORT void JNICALL Java_StringTest_showParms0  (JNIEnv *env, jobject obj, jstring s1, jstring s2){  const char* szStr1 = (*env)->GetStringUTFChars( env, s1, 0 );  const char* szStr2 = jstringToWindows( env, s2 );  MessageBox( HWND_DESKTOP, (LPCSTR)szStr1, (LPCSTR)"String1", 0 );  MessageBox( HWND_DESKTOP, (LPCSTR)szStr2, (LPCSTR)"String2", 0 );  (*env)->ReleaseStringUTFChars( env, s1, szStr1 );}char* jstringToWindows( JNIEnv* env, jstring jstr ){  int length = (*env)->GetStringLength( env, jstr );  const jchar* jcstr = (*env)->GetStringChars( env, jstr, 0 );  char* rtn = (char*)malloc( length*2+1 );  int size = 0;  size = WideCharToMultiByte( CP_ACP, 0, (LPCWSTR)jcstr, length, rtn,                           (length*2+1), NULL, NULL );  if( size <= 0 )    return NULL;  (*env)->ReleaseStringChars( env, jstr, jcstr );  rtn[size] = 0;  return rtn;}GetStringUTFChars创建c格式的字符串,GetStringChars创建unicode的字符串,windows传到java的string MultiByteToWideChar 转化windows string 到 Unicode character arrayjstring WindowsTojstring( JNIEnv* env, char* str ){  jstring rtn = 0;  int slen = strlen(str);  wchar_t* buffer = 0;  if( slen == 0 )    rtn = (*env)->NewStringUTF( env, str ); //UTF ok since empty string  else  {    int length =       MultiByteToWideChar( CP_ACP, 0, (LPCSTR)str, slen, NULL, 0 );    buffer = malloc( length*2 + 1 );    if( MultiByteToWideChar( CP_ACP, 0, (LPCSTR)str, slen,         (LPWSTR)buffer, length ) >0 )      rtn = (*env)->NewString( env, (jchar*)buffer, length );  }  if( buffer )   free( buffer );  return rtn;}MultiByteToWideChar被调用两次,第一次是Unicode character字符的个数,用于内存分配,第二次是进行转换,NewString 创建java的String,下面给出c中如何调用java类,使用命令行参数import java.awt.*;import java.awt.event.*;public class MyApp extends Frame{  public MyApp( String[] msgs )  {    super( "My Java App" );    Label label = new Label();    String labelMsg = new String();    for( int i=0; i < msgs.length; i++ )      labelMsg += msgs[i] + " ";    label.setText( labelMsg );    add( label );  }  public static void main( String[] args )  {    MyApp frame = new MyApp( args );    frame.addWindowListener( new WindowAdapter()    {      public void windowClosing(WindowEvent e) {System.exit(0);}    });    frame.setSize(200,200);    frame.setVisible(true);  }}#include <windows.h>#include <stdio.h>#include <jni.h>#define MAIN_CLASS  "MyApp"int APIENTRY WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,     LPSTR lpCmdLine, int nCmdShow){  JNIEnv*        env;  JavaVM*        jvm;  JDK1_1InitArgs vmargs;  jint           rc;  jclass         cls;  jmethodID      mainId;  /* get CLASSPATH environment variable setting */  char* szClasspath = getenv( "CLASSPATH" );  vmargs.version = 0x00010001;  /* version 1.1 */  JNI_GetDefaultJavaVMInitArgs( &vmargs );  /* init vmargs */  /* the classpath returned by JNI_GetDefaultJavaVMInitArgs is wrong */  vmargs.classpath = szClasspath;  rc = JNI_CreateJavaVM( &jvm, &env, &vmargs );  /* create JVM */  if( rc < 0 )    return 1;  /* load the class containing the static main() method */  cls = (*env)->FindClass( env, MAIN_CLASS );  if( cls == 0 )    return 1;  /* find the main() method */  mainId = (*env)->GetStaticMethodID(env, cls, "main",     "([Ljava/lang/String;)V");  if( mainId == 0 )    return 1; /* error */  /* setup the parameters to pass to main() */  {    jstring        str;    jobjectArray   args;    int i=0;    args = (*env)->NewObjectArray(env, __argc-1,         (*env)->FindClass(env, "java/lang/String"), 0);    for( i=1; i<__argc; i++ )    {      str = (*env)->NewStringUTF( env, __argv[i] );      (*env)->SetObjectArrayElement(env, args, i-1, str);    }    (*env)->CallStaticVoidMethod(env, cls, mainId, args); /* call main() */  }  (*jvm)->DestroyJavaVM( jvm );  /* kill JVM */  return 0;}将 str = (*env)->NewStringUTF( env, __argv[i] ); 换为 str = Windows2jstring( env, __argv[i] ); 解决中文问题。


阅读全文(10371) | 回复(0) | 编辑 | 精华
 



发表评论:
昵称:
密码:
主页:
标题:
验证码:  (不区分大小写,请仔细填写,输错需重写评论内容!)



站点首页 | 联系我们 | 博客注册 | 博客登陆

Sponsored By W3CHINA
W3CHINA Blog 0.8 Processed in 0.063 second(s), page refreshed 144320684 times.
《全国人大常委会关于维护互联网安全的决定》  《计算机信息网络国际联网安全保护管理办法》
苏ICP备05006046号