2015-08-25 5 views
10

जावा स्ट्रिंग को जेएनआई कोड में एक वास्तविक यूटीएफ -8 बाइट सरणी में परिवर्तित करने का कोई आसान तरीका है?जावा में वास्तविक यूटीएफ -8 अक्षरों को प्राप्त करना जेएनआई

दुर्भाग्य से GetStringUTFChars() लगभग क्या आवश्यक है लेकिन काफी नहीं है, यह एक "संशोधित" यूटीएफ -8 बाइट अनुक्रम देता है। मुख्य अंतर यह है कि एक संशोधित यूटीएफ -8 में कोई भी शून्य वर्ण नहीं होता है (इसलिए आप एक एएनएसआई सी नल टर्मिनेटेड स्ट्रिंग का इलाज कर सकते हैं) लेकिन एक और अंतर यह प्रतीत होता है कि यूनिकोड पूरक पूरक जैसे इमोजी का इलाज किया जाता है।

यू + 1 एफ 604 "खुले मुंह और स्माइलिंग आइज़ के साथ स्माइलिंग फेस" जैसे एक चरित्र को सरोगेट जोड़ी (दो यूटीएफ -16 वर्ण यू + डी 83 डी यू + डीई 04) के रूप में संग्रहीत किया जाता है और इसमें 4-बाइट यूटीएफ -8 समकक्ष होता है F0 की 9 फ 98 84, और कहा कि बाइट क्रम कि मैं मिलता है अगर मैं UTF-8 में स्ट्रिंग परिवर्तित जावा में:

char[] c = Character.toChars(0x1F604); 
    String s = new String(c); 
    System.out.println(s); 
    for (int i=0; i<c.length; ++i) 
     System.out.println("c["+i+"] = 0x"+Integer.toHexString(c[i])); 
    byte[] b = s.getBytes("UTF-8"); 
    for (int i=0; i<b.length; ++i) 
     System.out.println("b["+i+"] = 0x"+Integer.toHexString(b[i] & 0xFF)); 

कोड ऊपर प्रिंट निम्नलिखित:

ग [ 0] = 0xd83d सी [1] = 0xde04 बी [0] = 0xf0 बी [1] = 0x9F ख [2] = 0x98 ख [3] = 0x84

हालांकि, अगर मैं एक देशी JNI विधि में पारित और GetStringUTFChars() फोन 'एस' मैं 6 बाइट्स मिलता है।

JNIEXPORT void JNICALL Java_EmojiTest_nativeTest(JNIEnv *env, jclass cls, jstring _s) 
{ 
    const char* sBytes = env->GetStringUTFChars(_s, NULL); 
    for (int i=0; sBytes[i]!=0; ++i) 
     fprintf(stderr, "%d: %02x\n", i, sBytes[i]); 
    env->ReleaseStringUTFChars(_s, sBytes); 
    return result; 
} 

0: किराए की जोड़ी पात्रों में से प्रत्येक एक 3-बाइट क्रम स्वतंत्र रूप से करने के लिए परिवर्तित किया जा रहा है एड 1: a0 2: bd 3: एड 4: B8 5: 84

Wikipedia UTF-8 article सुझाव देता है कि GetStringUTFChars() वास्तव में यूटीएफ -8 के बजाय सीईएसयू -8 लौटाता है।

CFStringRef str = CFStringCreateWithCString(NULL, path, kCFStringEncodingUTF8); 
CFURLRef url = CFURLCreateWithFileSystemPath(NULL, str, kCFURLPOSIXPathStyle, false); 

मुझे लगता है मैं एक बाइट [] के बजाय एक स्ट्रिंग लेने के लिए अपने सभी JNI तरीकों को बदल सकता है लगता है और UTF कार्य करें: बदले में ऐसा इसलिए है क्योंकि यह एक वैध UTF-8 अनुक्रम नहीं है दुर्घटना करने के लिए अपने देशी मैक कोड का कारण बनता है -8 जावा में रूपांतरण लेकिन यह थोड़ा बदसूरत लगता है, क्या कोई बेहतर समाधान है?

उत्तर

17

यह स्पष्ट रूप से जावा दस्तावेज में समझाया गया है:

JNI Functions

GetStringUTFChars

const char * GetStringUTFChars(JNIEnv *env, jstring string, jboolean *isCopy); 

बाइट्स की एक सरणी के लिए सूचक रिटर्न संशोधित UTF-8 एन्कोडिंग में स्ट्रिंग का प्रतिनिधित्व। यह सरणी तब तक वैध है जब तक इसे रिलीज़स्ट्रिंगटफ़ाफ़र() द्वारा जारी नहीं किया जाता है।

Modified UTF-8

JNI संशोधित का उपयोग करता है UTF-8 तार विभिन्न स्ट्रिंग प्रकार का प्रतिनिधित्व करने के लिए। संशोधित यूटीएफ -8 स्ट्रिंग जावा वीएम द्वारा उपयोग किए जाने वाले समान हैं।संशोधित यूटीएफ -8 तारों को एन्कोड किया गया है ताकि चरित्र अनुक्रम जिनमें केवल गैर-शून्य ASCII वर्ण शामिल हों, प्रति वर्ण केवल एक बाइट का उपयोग करके प्रदर्शित किया जा सकता है, लेकिन सभी यूनिकोड वर्णों का प्रतिनिधित्व किया जा सकता है।

रेंज के सभी पात्र \u0001\u007F को इस प्रकार, एक एकल बाइट का प्रतिनिधित्व कर रहे:

table1

बाइट में डेटा के सात बिट्स चरित्र का प्रतिनिधित्व के मूल्य में दे।

रेंज में नल केरेक्टर ('\u0000') और वर्ण '\u0080''\u07FF' के बाइट्स x और y एक जोड़ी का प्रतिनिधित्व कर रहे:

table2

बाइट्स मूल्य ((x & 0x1f) << 6) + (y & 0x3f) साथ चरित्र को दर्शाते हैं। रेंज में

वर्ण '\u0800''\uFFFF' करने से 3 बाइट्स एक्स, वाई, और z प्रतिनिधित्व कर रहे हैं:

table3

मूल्य ((x & 0xf) << 12) + ((y & 0x3f) << 6) + (z & 0x3f) साथ चरित्र बाइट्स का प्रतिनिधित्व करती है।

यू + एफएफएफएफ (तथाकथित पूरक पात्रों) के ऊपर कोड बिंदु वाले अक्षर उनके यूटीएफ -16 प्रतिनिधित्व के दो सरोगेट कोड इकाइयों को अलग से एन्कोडिंग द्वारा दर्शाए जाते हैं। प्रत्येक सरोगेट कोड इकाइयों को तीन बाइट्स द्वारा दर्शाया जाता है। इसका मतलब यह है, अनुपूरक वर्ण, छह बाइट्स, u, v प्रतिनिधित्व कर रहे हैं डब्ल्यू, एक्स, वाई, और z:

table4

मूल्य 0x10000+((v&0x0f)<<16)+((w&0x3f)<<10)+(y&0x0f)<<6)+(z&0x3f) साथ चरित्र छह बाइट्स का प्रतिनिधित्व करती है।

मल्टीबाइट वर्णों के बाइट वर्ग फ़ाइल में बड़े-अंत (उच्च बाइट पहले) क्रम में संग्रहीत किए जाते हैं।

इस प्रारूप और मानक यूटीएफ -8 प्रारूप के बीच दो अंतर हैं। सबसे पहले, शून्य चरित्र (चार) 0 को एक-बाइट प्रारूप के बजाय दो-बाइट प्रारूप का उपयोग करके एन्कोड किया गया है। इसका मतलब है कि संशोधित यूटीएफ -8 तारों में कभी भी एम्बेडेड नल नहीं होते हैं। दूसरा, मानक यूटीएफ -8 के केवल एक-बाइट, दो-बाइट, और तीन-बाइट प्रारूपों का उपयोग किया जाता है। जावा वीएम मानक यूटीएफ -8 के चार-बाइट प्रारूप को नहीं पहचानता है; यह के बजाय अपने दो-बार-तीन-बाइट प्रारूप का उपयोग करता है।

मानक यूटीएफ -8 प्रारूप के बारे में अधिक जानकारी के लिए, यूनिकोड मानक, संस्करण 4.0 के खंड 3.9 यूनिकोड एन्कोडिंग फॉर्म देखें।

के बाद से U + 1F604 एक अनुपूरक चरित्र है, और जावा में UTF-8 के 4 बाइट एन्कोडिंग प्रारूप का समर्थन नहीं करता, U + 1F604 UTF-16 के किराए की जोड़ी U+D83D U+DE04 3 का उपयोग एन्कोडिंग द्वारा संशोधित UTF-8 में प्रतिनिधित्व किया है बाइट प्रति सरोगेट, इस प्रकार कुल 6 बाइट्स।

तो, आपके सवाल का जवाब देना ...

वहाँ एक आसान तरीका JNI कोड में एक सच्चे UTF-8 बाइट सरणी के लिए एक जावा स्ट्रिंग परिवर्तित करने के लिए है?

आप या तो:

  1. उपयोग GetStringChars() मूल UTF-16 एन्कोडेड वर्ण प्राप्त करने के लिए, और फिर उस से अपना खुद का UTF-8 बाइट सरणी पैदा करते हैं। यूटीएफ -16 से यूटीएफ -8 में रूपांतरण हाथ से लागू करने के लिए एक बहुत ही सरल एल्गोरिदम है।

    JNIEXPORT void JNICALL Java_EmojiTest_nativeTest(JNIEnv *env, jclass cls, jstring _s) 
    { 
        const jclass stringClass = env->GetObjectClass(_s); 
        const jmethodID getBytes = env->GetMethodID(stringClass, "getBytes", "(Ljava/lang/String;)[B"); 
    
        const jstring charsetName = env->NewStringUTF("UTF-8"); 
        const jbyteArray stringJbytes = (jbyteArray) env->CallObjectMethod(_s, getBytes, charsetName); 
        env->DeleteLocalRef(charsetName); 
    
        const jsize length = env->GetArrayLength(stringJbytes); 
        const jbyte* pBytes = env->GetByteArrayElements(stringJbytes, NULL); 
    
        for (int i = 0; i < length; ++i) 
         fprintf(stderr, "%d: %02x\n", i, pBytes[i]); 
    
        env->ReleaseByteArrayElements(stringJbytes, pBytes, JNI_ABORT); 
        env->DeleteLocalRef(stringJbytes); 
    } 
    

विकिपीडिया UTF-8:

  • String.getBytes(String charsetName) विधि आह्वान करने के लिए एक UTF-8 बाइट सरणी jstring वस्तु, जैसे सांकेतिक शब्दों में बदलना करने के लिए अपने JNI कोड जावा में वापस कॉल है लेख पता चलता है कि GetStringUTFChars() वास्तव में रिटर्न सेसु-8 के बजाय UTF-8

  • जावा के संशोधित UTF-8 बिल्कुल के समान नहीं है CESU-8:

    सेसु-8 जावा के संशोधित UTF-8 के समान है लेकिन NUL चरित्र (U + 0000) की विशेष एन्कोडिंग नहीं है।

    संबंधित मुद्दे