国产一级a片免费看高清,亚洲熟女中文字幕在线视频,黄三级高清在线播放,免费黄色视频在线看

打開APP
userphoto
未登錄

開通VIP,暢享免費電子書等14項超值服

開通VIP
JNI 中文處理問題小結(jié)

作者:大衛(wèi)的思維空間

  由于工作關(guān)系,需要利用JNI在C++與Java程序之間進行方法調(diào)用和數(shù)據(jù)傳遞,但以前總是在英文環(huán)境下工作,對中文(其他語言編碼同理)問題反倒沒有太關(guān)注,最近抽了點時間研究了一下,將自己的體會整理如下,供大家討論或參考。
在進一步討論之前,有幾點基礎(chǔ)知識需要說明:

  1. 在Java內(nèi)部,所有的字符串編碼采用的是Unicode即UCS-2。Unicode是用兩個字節(jié)表示每個字符的字符編碼方案。Unicode有一個特性:它包括了世界上所有的字符字形。所以,各個地區(qū)的語言都可以建立與Unicode的映射關(guān)系,而Java正是利用了這一點以達到異種語言之間的轉(zhuǎn)換;
  2. UTF-8是另一種不同于UCS-2/UCS-4的編碼方案,其中UTF代表UCS Transformation Format,它采用變長的方式進行編碼,編碼長度可以是1~3(據(jù)說理論上最長可以到6,不懂)。
    由于UCS-2/UCS-4編碼定長的原因,編碼產(chǎn)生的字符串會包含一些特殊的字符,如\0(即0x0,所有0~256的字符Unicode編碼的第一個字節(jié)),這在有些情況下(如傳輸或解析時)會給我們帶來一些麻煩,而且對于一般的英文字母浪費了太多的空間,此外,據(jù)說UTF-8還有Unicode所沒有的糾錯能力(不懂!),因此,Unicode往往只是被用作一種中間碼,用于邏輯表示。關(guān)于Unicode/UTF-8的更多信息,見參考1;

  Java中文亂碼問題在很多情況下都可能發(fā)生:不同應(yīng)用間,不同平臺間等等,但以上問題已有大量優(yōu)秀的文章討論過,這里不作深入探討,詳見參考2、3、4、5。下面簡要總結(jié)一下:

  1. 當(dāng)我們使用默認(rèn)編碼方式保存源文件時,文件內(nèi)容實際上是按照我們的系統(tǒng)設(shè)定進行編碼保存的,這個設(shè)定值即file.encoding可以通過下面的程序獲得:
    public class Encoding {    public static void main(String[] args) {        System.out.println(System.getProperty("file.encoding"));    }}
    javac在不指定encoding參數(shù)時,如果區(qū)域設(shè)定不正確,則可能造成編/解碼錯誤,這個問題在編譯一個從別的環(huán)境傳過來的文件時可能發(fā)生;
  2. 2、雖然在Java內(nèi)部(即運行期間,Runtime)字符串是以Unicode形式存在的,但在class文件中信息是以UTF-8形式存儲的(Unicode僅被用作邏輯表示中間碼) ;
  3. 對于Web應(yīng)用,以Tomcat為例,JSP/Servlet引擎提供的JSP轉(zhuǎn)換工具(jspc)搜索JSP文件中用<%@ page contentType ="text/html; charset=<Jsp-charset>"%>指定的charset。如果在JSP文件中未指定<Jsp-charset>,則取系統(tǒng)默認(rèn)的file.encoding(這個值在中文平臺上是GBK),可通過控制面板的Regional Options進行修改;jspc用相當(dāng)于“javac –encoding <Jsp-charset>”的命令解釋JSP文件中出現(xiàn)的所有字符,包括中文字符和ASCII字符,然后把這些字符轉(zhuǎn)換成Unicode字符,再轉(zhuǎn)化成UTF-8格式,存為JAVA文件。
    我曾經(jīng)偶然將jsp文件存成UTF-8,而在文件內(nèi)部使用的charset卻是GB2312,結(jié)果運行時總是無法正常顯示中文,后來轉(zhuǎn)存為默認(rèn)編碼方式才正常。只要文件存儲格式與JSP開頭的charset設(shè)置一致,就都可以正常顯示(不過將文件保存成UTF-16的情況下我還沒有試驗成功);
  4. 在XML文件中,encoding表示的是文件本身的編碼方式,如果這個參數(shù)設(shè)定與文件本身實際的編碼方式不一致的話,則可能解碼失敗,所以應(yīng)該總是將encoding設(shè)置成與文件編碼方式一致的值;而JSP/HTML的charset則表示按照何種字符集來解碼從文件中讀取出來的字符串(在理解中文問題時應(yīng)該把字符串理解成一個二進制或16進制的串,按照不同的charset可能映射成不同的字符)。
    我曾經(jīng)在網(wǎng)上就encoding的具體含義跟別人討論過:如果encoding指的是文件本身的編碼方式,那么讀取該文件的應(yīng)用程序在不知道encoding設(shè)置的情況下如何正確解讀該文件呢?
    根據(jù)討論及個人理解,處理程序(如jspc)總是按ISO8859-1來讀取輸入文件,然后檢查文件開始的幾個字節(jié)(即Byte Order Mark,BOM,具體如何判斷,可以參考Tomcat源碼$SOURCE_DIR\jasper\jasper2\src\share\org\apache\jasper\xmlparser\XMLEncodingDetector.java的getEncodingName方法,在JSP Specification的Page Character Encoding一節(jié)也有詳細(xì)論述)以探測文件是以何種格式保存的,當(dāng)解析到encoding選項時,若encoding設(shè)置與文件實際保存格式不一致,會嘗試進行轉(zhuǎn)換,但這種轉(zhuǎn)換可能在文件實際以ISO8859-1/UTF-8等單字節(jié)編碼而encoding被設(shè)置成Unicode、UTF-16等雙字節(jié)編碼時發(fā)生錯誤。

下面重點討論JNI中在C++程序與Java程序間進行數(shù)據(jù)傳遞時需要注意的問題。

  在JNI中jstring采用的是UCS-2編碼,與Java中String的編碼方式一致。但是在C++中,字符串是用char(8位)或者wchar_t(16位,Unicode編碼與jchar一致,但并非所有開發(fā)平臺上都是Unicode編碼,詳見參考6),下面的程序證明了這一點(編譯環(huán)境:VC6):

#include <iostream>using namespace std;int main(){    locale loc( "Chinese-simplified" );    //locale loc( "chs" );    //locale loc( "ZHI" );    //locale loc( ".936" );    wcout.imbue( loc );    wcout << L"中文" << endl; //若沒有L,會出問題    wchar_t wch[] = {0x4E2D, 0x6587, 0x0}; //"中文"二字的Unicode編碼    wcout << wch << endl;    return 0;}
JNI提供了幾個方法來實現(xiàn)jstring與char/wchar_t之間的轉(zhuǎn)換。
jsize GetStringLength(jstring str)const jchar *GetStringChars(jstring str, jboolean *isCopy)void ReleaseStringChars(jstring str, const jchar *chars)
此外,為了便于以UTF-8方式進行傳輸、存儲,JNI還提供了幾個操作UTF格式的方法:
jsize GetStringUTFLength(jstring str)const char* GetStringUTFChars(jstring str, jboolean *isCopy)void ReleaseStringUTFChars(jstring str, const char* chars)
GetStringChars返回的是Unicode格式的編碼串,而GetStringUTFChars返回的是UTF-8格式的編碼串。要創(chuàng)建一個jstring,可以用如下方式:
jstring NewJString( JNIEnv * env, LPCTSTR str ){    if (!env || !str)        return 0;    int slen = strlen(str);    jchar * buffer = new jchar[slen];    int len = MultiByteToWideChar(CP_ACP, 0, str, strlen(str), buffer, slen);    if (len > 0 && len < slen)        buffer[len] = 0;    jstring js = env->NewString(buffer, len);    delete [] buffer;    return js;}
而要將一個jstring對象轉(zhuǎn)為一個char字符串?dāng)?shù)組,可以:
int JStringToChar( JNIEnv * env, jstring str, LPTSTR desc, int desc_len ){    int len = 0;    if (desc == NULL || str == NULL)        return -1;    // Check buffer size    if (env->GetStringLength(str) * 2 + 1 > desc_len)    {        return -2;    }    memset(desc, 0, desc_len);    const wchar_t * w_buffer = env->GetStringChars(str, 0);    len = WideCharToMultiByte(CP_ACP, 0, w_buffer, wcslen(w_buffer) + 1, desc, desc_len, NULL, NULL);    env->ReleaseStringChars(str, w_buffer);    if (len > 0 && len < desc_len)        desc[len] = 0;    return strlen(desc);}
  當(dāng)然,按照上面的分析,你也可以直接將GetStringChars的返回結(jié)果作為wchar_t串來進行操作。或者,如果你愿意,你也可以將GetStringUTFChars的結(jié)果通過MultiByteToWideChar轉(zhuǎn)換為UCS2編碼串,再通過WideCharToMultiByte轉(zhuǎn)換為多字節(jié)串。
const char* pstr = env->GetStringUTFChars(str, false);int nLen = MultiByteToWideChar( CP_UTF8, 0, pstr, -1, NULL, NULL );//得到UTF-8編碼的字符串長度LPWSTR lpwsz = new WCHAR[nLen];    MultiByteToWideChar( CP_UTF8, 0, pstr, -1, lpwsz, nLen );//轉(zhuǎn)換的結(jié)果是UCS2格式的編碼串int nLen1 = WideCharToMultiByte( CP_ACP, 0, lpwsz, nLen, NULL, NULL, NULL, NULL );    LPSTR lpsz = new CHAR[nLen1];WideCharToMultiByte( CP_ACP, 0, lpwsz, nLen, lpsz, nLen1, NULL, NULL );//將UCS2格式的編碼串轉(zhuǎn)換為多字節(jié)cout << "Out:" << lpsz << endl;delete [] lpwsz; delete [] lpsz;
  當(dāng)然,我相信很少有人想要或者需要這么做。這里需要注意一點,GetStringChars的返回值是jchar,而GetStringUTFChars的返回值是const char*。除了上面的辦法外,當(dāng)需要經(jīng)常在jstring和char*之間進行轉(zhuǎn)換時我們還有一個選擇,那就是下面的這個類。這個類本來是一個叫Roger S. Reynolds的老外提供的,想法非常棒,但用起來卻不太靈光,因為作者將考慮的重心放在UTF格式串上,但在實際操作中,我們往往使用的卻是ACP(ANSI code page)串。下面是原作者的程序:
class UTFString {private:    UTFString (); // Default ctor - disallowed public:    // Create a new instance from the specified jstring     UTFString(JNIEnv* env, const jstring& str) :        mEnv (env),        mJstr (str),        mUtfChars ((char* )mEnv->GetStringUTFChars (mJstr, 0)),        mString (mUtfChars) { }    // Create a new instance from the specified string     UTFString(JNIEnv* env, const string& str) :        mEnv (env),        mString (str),        mJstr (env->NewStringUTF (str.c_str ())),        mUtfChars ((char* )mEnv->GetStringUTFChars (mJstr, 0)) { }    // Create a new instance as a copy of the specified UTFString     UTFString(const UTFString& rhs) :        mEnv (rhs.mEnv),        mJstr (mEnv->NewStringUTF (rhs.mUtfChars)),        mUtfChars ((char* )mEnv->GetStringUTFChars (mJstr, 0)),        mString (mUtfChars) { }    // Delete the instance and release allocated storage     ~UTFString() { mEnv->ReleaseStringUTFChars (mJstr, mUtfChars); }    // assign a new value to this instance from the given string     UTFString & operator =(const string& rhs) {        mEnv->ReleaseStringUTFChars (mJstr, mUtfChars);        mJstr = mEnv->NewStringUTF (rhs.c_str ());        mUtfChars = (char* )mEnv->GetStringUTFChars (mJstr, 0);        mString = mUtfChars;        return *this;    }    // assign a new value to this instance from the given char*     UTFString & operator =(const char* ptr) {        mEnv->ReleaseStringUTFChars (mJstr, mUtfChars);        mJstr = mEnv->NewStringUTF (ptr);        mUtfChars = (char* )mEnv->GetStringUTFChars (mJstr, 0);        mString = mUtfChars;        return *this;    }    // Supply operator methods for converting the UTFString to a string     // or char*, making it easy to pass UTFString arguments to functions     // that require string or char* parameters.     string & GetString() { return mString; }    operator string() { return mString; }    operator const char* () { return mString.c_str (); }    operator jstring() { return mJstr; }private:    JNIEnv* mEnv;    // The enviroment pointer for this native method.     jstring mJstr;   // A copy of the jstring object that this UTFString represents     char* mUtfChars; // Pointer to the data returned by GetStringUTFChars     string mString;  // string buffer for holding the "value" of this instance };
我將它改了改:
class JNIString {private:    JNIString (); // Default ctor - disallowedpublic:    // Create a new instance from the specified jstring    JNIString(JNIEnv* env, const jstring& str) :        mEnv (env) {        const jchar* w_buffer = env->GetStringChars (str, 0);        mJstr = env->NewString (w_buffer,                                wcslen (w_buffer)); // Deep Copy, in usual case we only need 						// Shallow Copy as we just need this class to 						// provide some convenience for handling jstring        mChars = new char[wcslen (w_buffer) * 2 + 1];        WideCharToMultiByte (CP_ACP, 0, w_buffer, wcslen (w_buffer) + 1, mChars, wcslen (w_buffer) * 2 + 1,                             NULL,   NULL);        env->ReleaseStringChars (str, w_buffer);        mString = mChars;    }    // Create a new instance from the specified string    JNIString(JNIEnv* env, const string& str) :        mEnv (env) {        int slen = str.length ();        jchar* buffer = new jchar[slen];        int len = MultiByteToWideChar (CP_ACP, 0, str.c_str (), str.length (), buffer, slen);        if (len > 0 && len < slen)            buffer[len] = 0;        mJstr = env->NewString (buffer, len);        delete [] buffer;        mChars = new char[str.length () + 1];        strcpy (mChars, str.c_str ());        mString.empty ();        mString = str.c_str ();    }    // Create a new instance as a copy of the specified JNIString    JNIString(const JNIString& rhs) :        mEnv (rhs.mEnv) {        const jchar* wstr = mEnv->GetStringChars (rhs.mJstr, 0);        mJstr = mEnv->NewString (wstr, wcslen (wstr));        mEnv->ReleaseStringChars (rhs.mJstr, wstr);        mChars = new char[strlen (rhs.mChars) + 1];        strcpy (mChars, rhs.mChars);        mString = rhs.mString.c_str ();    }    // Delete the instance and release allocated storage    ~JNIString() { delete [] mChars; }    // assign a new value to this instance from the given string    JNIString & operator =(const string& rhs) {        delete [] mChars;        int slen = rhs.length ();        jchar* buffer = new jchar[slen];        int len = MultiByteToWideChar (CP_ACP, 0, rhs.c_str (), rhs.length (), buffer, slen);        if (len > 0 && len < slen)            buffer[len] = 0;        mJstr = mEnv->NewString (buffer, len);        delete [] buffer;        mChars = new char[rhs.length () + 1];        strcpy (mChars, rhs.c_str ());        mString = rhs.c_str ();        return *this;    }    // Supply operator methods for converting the JNIString to a string    // or char*, making it easy to pass JNIString arguments to functions    // that require string or char* parameters.    string & GetString() { return mString; }    operator string() { return mString; }    operator const char* () { return mString.c_str (); }    operator jstring() { return mJstr; }private:    JNIEnv* mEnv;   // The enviroment pointer for this native method.    jstring mJstr;  // A copy of the jstring object that this JNIString represents    char* mChars;   // Pointer to a ANSI code page char array    string mString; // string buffer for holding the "value" of this instance (ANSI code page)};
  后者除了將面向UTF編碼改成了面向ANSI編碼外,還去掉了operator =(const char* ptr)的定義,因為 operator =(const string& rhs)可以在需要的時候替代前者而無需任何額外編碼。(因為按照C++規(guī)范,const reference可以自動轉(zhuǎn)換,詳見本人另一文章“關(guān)于 const reference 的幾點說明”)
  如果你愿意,給JNIString再加個JNIString(JNIEnv* env, const wstring& str)和一個operator =(const wstring& rhs)操作符重載就比較完美了,:),很簡單,留給用得到的朋友自己加吧。
下面是一個使用該類的例子(真正跟用于演示的code很少,大部分都是些routine code,:)):
#include <iostream>#include <string>#include <assert.h>#include <jni.h>using namespace std;int main() {    int res;    JavaVM* jvm;    JNIEnv* env;    JavaVMInitArgs vm_args;    JavaVMOption options[3];    options[0].optionString = "-Djava.compiler=NONE";    options[1].optionString = "-Djava.class.path=.;.."; // .. is specially for this project    options[2].optionString = "-verbose:jni";    vm_args.version = JNI_VERSION_1_4;    vm_args.nOptions = 3;    vm_args.options = options;    vm_args.ignoreUnrecognized = JNI_TRUE;    res = JNI_CreateJavaVM (& jvm, (void* * )& env, & vm_args);    if (res < 0) {        fprintf (stderr, "Can''t create Java VM\n");        return 1;    }    jclass cls = env->FindClass ("jni/test/Demo");    assert (0 != cls);    jmethodID mid = env->GetMethodID (cls, "", "(Ljava/lang/String;)V");    assert (0 != mid);    wchar_t* p = L"中國";    jobject obj = env->NewObject (cls, mid, env->NewString (reinterpret_cast (p), wcslen (p)));    assert (0 != obj);    mid = env->GetMethodID (cls, "getMessage", "()Ljava/lang/String;");    assert (0 != mid);    jstring str = (jstring)env->CallObjectMethod (obj, mid);    // use JNIString for easier handling.    JNIString jnistr (env, str);    cout << "JNIString:" << jnistr.GetString () << endl;    jnistr = "中文";    cout << jnistr.GetString () << endl;    jvm->DestroyJavaVM ();    fprintf (stdout, "Java VM destory.\n");    return 0;}
參考資料:
  1. UTF-8 and Unicode FAQ for Unix/Linuxs,http://www.cl.cam.ac.uk/~mgk25/unicode.html,
    其中文翻譯見http://www.linuxforum.net/books/UTF-8-Unicode.html
  2. 深入剖析Java編程中的中文問題及建議最優(yōu)解決方法,http://blog.csdn.net/abnerchai/archive/2004/04/28/18576.aspx
  3. 關(guān)于Java中文問題的幾條分析原則,http://www-900.ibm.com/developerWorks/cn/java/l-javachinese/index.shtml
  4. Java 編程技術(shù)中漢字問題的分析及解決,http://www-900.ibm.com/developerWorks/cn/java/java_chinese/index.shtml
  5. 深入剖析JSP和Servlet對中文的處理過程,http://blog.csdn.net/deuso/archive/2005/12/01/541511.aspx
  6. 寬字符標(biāo)量L"xx"在VC6.0/7.0和GNU g++中的不同實現(xiàn),http://blog.vckbase.com/smileonce/archive/2004/12/09/1972.html
  7. XML Encoding,http://www.w3schools.com/xml/xml_encoding.asp

本站僅提供存儲服務(wù),所有內(nèi)容均由用戶發(fā)布,如發(fā)現(xiàn)有害或侵權(quán)內(nèi)容,請點擊舉報。
打開APP,閱讀全文并永久保存 查看更多類似文章
猜你喜歡
類似文章
jni中字符轉(zhuǎn)換中文亂碼的處理方法
字符編碼轉(zhuǎn)換_進制轉(zhuǎn)換(GB2312,GBK,JNI,HexTOStr) (轉(zhuǎn))
JNI相關(guān)類型轉(zhuǎn)換
做了兩天,用jni的一點感受
char 轉(zhuǎn)wchar_t 及wchar_t轉(zhuǎn)char - 雨點的日志 - 網(wǎng)易博客
WCHAR,CHAR,TCHAR的區(qū)別
更多類似文章 >>
生活服務(wù)
分享 收藏 導(dǎo)長圖 關(guān)注 下載文章
綁定賬號成功
后續(xù)可登錄賬號暢享VIP特權(quán)!
如果VIP功能使用有故障,
可點擊這里聯(lián)系客服!

聯(lián)系客服