2010-06-01 11 views
6

मान लीजिए कि मेरे पास स्मृति में संग्रहीत यूटीएफ -8 सामग्री है, तो मैं सूचक का उपयोग कर वर्णों को कैसे पढ़ूं? मुझे लगता है कि मुझे एक बहु-बाइट चरित्र का संकेत देने वाले 8 वें बिट के लिए देखने की ज़रूरत है, लेकिन मैं अनुक्रम को एक वैध यूनिकोड चरित्र में कैसे बदलूं? इसके अलावा, wchar_t एक यूनिकोड चरित्र को स्टोर करने के लिए उचित प्रकार है?मैं पॉइंटर के माध्यम से यूटीएफ -8 अक्षरों को कैसे पढ़ूं?

यह मेरे मन में है: आप को डिकोड करने की जरूरत है

 

    wchar_t readNextChar (char*& p) 
    { 
     wchar_t unicodeChar; 
     char ch = *p++; 

     if ((ch & 128) != 0) 
     { 
      // This is a multi-byte character, what do I do now? 
      // char chNext = *p++; 
      // ... but how do I assemble the Unicode character? 
      ... 
     } 
     ... 
     return unicodeChar; 
    } 
 
+3

"यूनिकोड चरित्र की चौड़ाई" की बात करने के लिए यह समझ में नहीं आता है। आपको एक एन्कोडिंग के लिए बसने की जरूरत है। आपके प्लेटफ़ॉर्म के आधार पर, 'wchar_t' अलग-अलग आकार का हो सकता है। यूनिक्स जैसी ओएस पर यह आमतौर पर 32 बिट है, इसलिए आप यूटीएफ -32 एन्कोडेड यूनिकोड वर्णों को स्टोर कर सकते हैं, विंडोज़ पर यह 16 बिट है, इसलिए यह यूटीएफ -16 एन्कोडेड यूनिकोड वर्ण ले सकता है। – sbi

+0

विस्तृत चरित्र लौटने के अलावा, आपके 'readNextChar' फ़ंक्शन को' p' को ठीक से अपडेट करने के लिए जानकारी प्रदान करनी होगी। यूटीएफ -8 (और यूटीएफ -16, उस मामले के लिए) परिवर्तनीय लंबाई एन्कोडिंग हैं और आपका कॉलर पॉइंटर में निरंतर या सरल वृद्धि नहीं कर सकता है। – mpez0

उत्तर

7

आप UTF- डिकोड करने के लिए है अपने unencoded यूटीएफ -32 प्रतिनिधित्व के लिए 8 बिट पैटर्न। यदि आप वास्तविक यूनिकोड कोडपॉइंट चाहते हैं, तो आपको 32-बिट डेटा प्रकार का उपयोग करना होगा।

विंडोज़ पर, wchar_t पर्याप्त नहीं है, क्योंकि यह केवल 16-बिट है। आपको इसके बजाय unsigned int या unsigned long का उपयोग करना होगा। इसके बजाय यूटीएफ -16 कोडुनिट्स से निपटने के दौरान wchar_t का उपयोग करें।

अन्य प्लेटफार्मों पर, wchar_t आमतौर पर 32 बिट होता है। लेकिन पोर्टेबल कोड लिखते समय, आपको wchar_t से दूर रहना चाहिए, सिवाय इसके कि जहां बिल्कुल जरूरी है (जैसे std::wstring)।

#define IS_IN_RANGE(c, f, l) (((c) >= (f)) && ((c) <= (l))) 

u_long readNextChar (char* &p) 
{ 
    // TODO: since UTF-8 is a variable-length 
    // encoding, you should pass in the input 
    // buffer's actual byte length so that you 
    // can determine if a malformed UTF-8 
    // sequence would exceed the end of the buffer... 

    u_char c1, c2, *ptr = (u_char*) p; 
    u_long uc = 0; 
    int seqlen; 
    // int datalen = ... available length of p ...;  

    /* 
    if(datalen < 1) 
    { 
     // malformed data, do something !!! 
     return (u_long) -1; 
    } 
    */ 

    c1 = ptr[0]; 

    if((c1 & 0x80) == 0) 
    { 
     uc = (u_long) (c1 & 0x7F); 
     seqlen = 1; 
    } 
    else if((c1 & 0xE0) == 0xC0) 
    { 
     uc = (u_long) (c1 & 0x1F); 
     seqlen = 2; 
    } 
    else if((c1 & 0xF0) == 0xE0) 
    { 
     uc = (u_long) (c1 & 0x0F); 
     seqlen = 3; 
    } 
    else if((c1 & 0xF8) == 0xF0) 
    { 
     uc = (u_long) (c1 & 0x07); 
     seqlen = 4; 
    } 
    else 
    { 
     // malformed data, do something !!! 
     return (u_long) -1; 
    } 

    /* 
    if(seqlen > datalen) 
    { 
     // malformed data, do something !!! 
     return (u_long) -1; 
    } 
    */ 

    for(int i = 1; i < seqlen; ++i) 
    { 
     c1 = ptr[i]; 

     if((c1 & 0xC0) != 0x80) 
     { 
      // malformed data, do something !!! 
      return (u_long) -1; 
     } 
    } 

    switch(seqlen) 
    { 
     case 2: 
     { 
      c1 = ptr[0]; 

      if(!IS_IN_RANGE(c1, 0xC2, 0xDF)) 
      { 
       // malformed data, do something !!! 
       return (u_long) -1; 
      } 

      break; 
     } 

     case 3: 
     { 
      c1 = ptr[0]; 
      c2 = ptr[1]; 

      switch (c1) 
      { 
       case 0xE0: 
        if (!IS_IN_RANGE(c2, 0xA0, 0xBF)) 
        { 
         // malformed data, do something !!! 
         return (u_long) -1; 
        } 
        break; 

       case 0xED: 
        if (!IS_IN_RANGE(c2, 0x80, 0x9F)) 
        { 
         // malformed data, do something !!! 
         return (u_long) -1; 
        } 
        break; 

       default: 
        if (!IS_IN_RANGE(c1, 0xE1, 0xEC) && !IS_IN_RANGE(c1, 0xEE, 0xEF)) 
        { 
         // malformed data, do something !!! 
         return (u_long) -1; 
        } 
        break; 
      } 

      break; 
     } 

     case 4: 
     { 
      c1 = ptr[0]; 
      c2 = ptr[1]; 

      switch (c1) 
      { 
       case 0xF0: 
        if (!IS_IN_RANGE(c2, 0x90, 0xBF)) 
        { 
         // malformed data, do something !!! 
         return (u_long) -1; 
        } 
        break; 

       case 0xF4: 
        if (!IS_IN_RANGE(c2, 0x80, 0x8F)) 
        { 
         // malformed data, do something !!! 
         return (u_long) -1; 
        } 
        break; 

       default: 
        if (!IS_IN_RANGE(c1, 0xF1, 0xF3)) 
        { 
         // malformed data, do something !!! 
         return (u_long) -1; 
        } 
        break;     
      } 

      break; 
     } 
} 

    for(int i = 1; i < seqlen; ++i) 
    { 
     uc = ((uc << 6) | (u_long)(ptr[i] & 0x3F)); 
    } 

    p += seqlen; 
    return uc; 
} 
+0

बिल्कुल सही, धन्यवाद! –

+0

@ रेमी और @ जेन: 'wchar_t' की सटीक चौड़ाई परिभाषित नहीं की गई है। जीसीसी (कम से कम लिनक्स पर) 'wchar_t' 32 बिट है, इसलिए यह निश्चित रूप से बहु-बाइट एन्कोडिंग के बिना यूनिकोड वर्ण रखने के लिए पर्याप्त होगा। – sbi

+0

मैंने अभी इस कोड को g ++ 3.3.4 का उपयोग करके संकलित किया है, और मैं बहुत प्रभावित हूं: कंपाइलर ने बड़े कोड 'कथन' कथन से सभी कोड को स्थानांतरित किया जहां 'seqlen' चर सेट हो जाता है। हो सकता है कि मूल कोड के लिए भी अधिक पठनीय बनने के लिए अच्छा होगा। –

2

UTF-8 आप एक UTF-8 पार्सर का विकास करते हैं की जरूरत है। यूटीएफ -8 एक परिवर्तनीय-लंबाई एन्कोडिंग (1 से 4 बाइट्स) है, इसलिए आपको वास्तव में एक पार्सर लिखना होगा जो मानक के अनुरूप है: उदाहरण के लिए wikipedia देखें।

यदि आप अपना खुद का पार्सर नहीं लिखना चाहते हैं, तो मैं लाइब्रेरी का उपयोग करने का सुझाव देता हूं। आप पाएंगे कि उदाहरण के लिए ग्लिब में (मैं व्यक्तित्व ने ग्लिब :: ustring, ग्लिब के सी ++ रैपर का उपयोग किया है) लेकिन किसी भी सामान्य सामान्य उद्देश्य पुस्तकालय में भी।

संपादित करें:

मुझे लगता है कि C++ 0x UTF-8 समर्थन भी शामिल होंगे, लेकिन मैं कोई विशेषज्ञ हूँ ...

my2c

1

इसके अलावा, उचित प्रकार एक भी यूनिकोड वर्ण स्टोर करने के लिए wchar_t है:

अधिक कुछ इस तरह का प्रयास करें?

लिनक्स पर, हाँ। विंडोज़ पर, wchar_t एक यूटीएफ -16 कोड इकाई का प्रतिनिधित्व करता है, जो आवश्यक रूप से एक चरित्र नहीं है।

आगामी सी ++ 0x मानक यूटीएफ -16 और यूटीएफ -32 का प्रतिनिधित्व करने के लिए char16_t और char32_t प्रकार प्रदान करेगा।

एक प्रणाली है जहाँ char32_t उपलब्ध नहीं है और wchar_t अपर्याप्त है पर हैं, तो uint32_t का उपयोग यूनिकोड वर्ण स्टोर करने के लिए।

4

यहां एक त्वरित मैक्रो कि UTF-8 बाइट्स

#define UTF8_CHAR_LEN(byte) ((0xE5000000 >> ((byte >> 3) & 0x1e)) & 3) + 1 

यह आपको आसान पार्सिंग के लिए UTF-8 वर्ण के आकार का पता लगाने में मदद मिलेगी ही गिना जाएगा।

+0

मददगार! Mblen() को प्रतिस्थापित करने का तेज़ तरीका, जब यह काम नहीं करता है। – southerton

1

यह मेरा समाधान है, शुद्ध एएनएसआई-सी में, कोने के मामलों के लिए एक इकाई परीक्षण सहित।

सावधान रहें कि int कम से कम 32 बिट चौड़े होना चाहिए। अन्यथा आपको codepoint की परिभाषा को बदलना होगा।

#include <assert.h> 
#include <errno.h> 
#include <stdio.h> 
#include <stdlib.h> 

typedef unsigned char byte; 
typedef unsigned int codepoint; 

/** 
* Reads the next UTF-8-encoded character from the byte array ranging 
* from {@code *pstart} up to, but not including, {@code end}. If the 
* conversion succeeds, the {@code *pstart} iterator is advanced, 
* the codepoint is stored into {@code *pcp}, and the function returns 
* 0. Otherwise the conversion fails, {@code errno} is set to 
* {@code EILSEQ} and the function returns -1. 
*/ 
int 
from_utf8(const byte **pstart, const byte *end, codepoint *pcp) { 
     size_t len, i; 
     codepoint cp, min; 
     const byte *buf; 

     buf = *pstart; 
     if (buf == end) 
       goto error; 

     if (buf[0] < 0x80) { 
       len = 1; 
       min = 0; 
       cp = buf[0]; 
     } else if (buf[0] < 0xC0) { 
       goto error; 
     } else if (buf[0] < 0xE0) { 
       len = 2; 
       min = 1 << 7; 
       cp = buf[0] & 0x1F; 
     } else if (buf[0] < 0xF0) { 
       len = 3; 
       min = 1 << (5 + 6); 
       cp = buf[0] & 0x0F; 
     } else if (buf[0] < 0xF8) { 
       len = 4; 
       min = 1 << (4 + 6 + 6); 
       cp = buf[0] & 0x07; 
     } else { 
       goto error; 
     } 

     if (buf + len > end) 
       goto error; 

     for (i = 1; i < len; i++) { 
       if ((buf[i] & 0xC0) != 0x80) 
         goto error; 
       cp = (cp << 6) | (buf[i] & 0x3F); 
     } 

     if (cp < min) 
       goto error; 

     if (0xD800 <= cp && cp <= 0xDFFF) 
       goto error; 

     if (0x110000 <= cp) 
       goto error; 

     *pstart += len; 
     *pcp = cp; 
     return 0; 

error: 
     errno = EILSEQ; 
     return -1; 
} 

static void 
assert_valid(const byte **buf, const byte *end, codepoint expected) { 
     codepoint cp; 

     if (from_utf8(buf, end, &cp) == -1) { 
       fprintf(stderr, "invalid unicode sequence for codepoint %u\n", expected); 
       exit(EXIT_FAILURE); 
     } 

     if (cp != expected) { 
       fprintf(stderr, "expected %u, got %u\n", expected, cp); 
       exit(EXIT_FAILURE); 
     } 
} 

static void 
assert_invalid(const char *name, const byte **buf, const byte *end) { 
     const byte *p; 
     codepoint cp; 

     p = *buf + 1; 
     if (from_utf8(&p, end, &cp) == 0) { 
       fprintf(stderr, "unicode sequence \"%s\" unexpectedly converts to %#x.\n", name, cp); 
       exit(EXIT_FAILURE); 
     } 
     *buf += (*buf)[0] + 1; 
} 

static const byte valid[] = { 
     0x00, /* first ASCII */ 
     0x7F, /* last ASCII */ 
     0xC2, 0x80, /* first two-byte */ 
     0xDF, 0xBF, /* last two-byte */ 
     0xE0, 0xA0, 0x80, /* first three-byte */ 
     0xED, 0x9F, 0xBF, /* last before surrogates */ 
     0xEE, 0x80, 0x80, /* first after surrogates */ 
     0xEF, 0xBF, 0xBF, /* last three-byte */ 
     0xF0, 0x90, 0x80, 0x80, /* first four-byte */ 
     0xF4, 0x8F, 0xBF, 0xBF /* last codepoint */ 
}; 

static const byte invalid[] = { 
     1, 0x80, 
     1, 0xC0, 
     1, 0xC1, 
     2, 0xC0, 0x80, 
     2, 0xC2, 0x00, 
     2, 0xC2, 0x7F, 
     2, 0xC2, 0xC0, 
     3, 0xE0, 0x80, 0x80, 
     3, 0xE0, 0x9F, 0xBF, 
     3, 0xED, 0xA0, 0x80, 
     3, 0xED, 0xBF, 0xBF, 
     4, 0xF0, 0x80, 0x80, 0x80, 
     4, 0xF0, 0x8F, 0xBF, 0xBF, 
     4, 0xF4, 0x90, 0x80, 0x80 
}; 

int 
main() { 
     const byte *p, *end; 

     p = valid; 
     end = valid + sizeof valid; 
     assert_valid(&p, end, 0x000000); 
     assert_valid(&p, end, 0x00007F); 
     assert_valid(&p, end, 0x000080); 
     assert_valid(&p, end, 0x0007FF); 
     assert_valid(&p, end, 0x000800); 
     assert_valid(&p, end, 0x00D7FF); 
     assert_valid(&p, end, 0x00E000); 
     assert_valid(&p, end, 0x00FFFF); 
     assert_valid(&p, end, 0x010000); 
     assert_valid(&p, end, 0x10FFFF); 

     p = invalid; 
     end = invalid + sizeof invalid; 
     assert_invalid("80", &p, end); 
     assert_invalid("C0", &p, end); 
     assert_invalid("C1", &p, end); 
     assert_invalid("C0 80", &p, end); 
     assert_invalid("C2 00", &p, end); 
     assert_invalid("C2 7F", &p, end); 
     assert_invalid("C2 C0", &p, end); 
     assert_invalid("E0 80 80", &p, end); 
     assert_invalid("E0 9F BF", &p, end); 
     assert_invalid("ED A0 80", &p, end); 
     assert_invalid("ED BF BF", &p, end); 
     assert_invalid("F0 80 80 80", &p, end); 
     assert_invalid("F0 8F BF BF", &p, end); 
     assert_invalid("F4 90 80 80", &p, end); 

     return 0; 
} 
+0

महाकाव्य विफल। पोस्टर सी ++ में है, और आपने कई सी ++ मुहावरों का उल्लंघन किया है, मैं भी गिनना शुरू नहीं कर सकता। – Puppy

+0

तो मैं आपके लिए गिनती करूंगा। (1) मैंने सी ++ हेडर के बजाय सी हेडर शामिल किए। (2) मैंने संदर्भों के बजाय पॉइंटर्स का इस्तेमाल किया। (3) मैंने नेमस्पेस का उपयोग नहीं किया, बल्कि इसके बजाय मेरे कार्यों को 'स्थिर' घोषित किया। (4) मैंने लूप वैरिएबल को फंक्शन-वाइड स्कोप के साथ घोषित किया। लेकिन दूसरी तरफ, मैंने अजीब प्रकार के नामों का आविष्कार नहीं किया ('u_long', 'u_char') और उन्हें असंगत रूप से इस्तेमाल किया (' u_char' बनाम 'uchar') और उन्हें घोषित किए बिना। मैं पूरी तरह से किसी भी प्रकार का कलाकार से बचने में कामयाब रहा (जिसे स्वीकार्य उत्तर बहुत उपयोग करता है, और यह सी-शैली भी है।) –

+0

और वैसे, संदर्भों के बजाय पॉइंटर्स का उपयोग इस मामले में पूरी तरह से जानबूझकर है, क्योंकि 'पस्टार्ट' और 'एंड' अन्यथा कॉलर की तरफ बहुत समान दिखेंगे। 'from_utf8 (प्रारंभ, अंत, और सीपी)'। किसी को यह कैसे अनुमान लगाया जाना चाहिए कि 'स्टार्ट' संशोधित हो जाती है और 'अंत' नहीं होती है? –

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