2012-10-12 173 views
5

我的問題很簡單,但我無法到目前爲止找到一個解決方案:如何在C/C++中將字符串從UTF8轉換爲Latin1?

我如何轉換utf-8編碼string到latin1的用C編碼string ++而不使用任何額外的庫像libiconv的?

到目前爲止我能找到的每個例子都是針對latin1到UTF8的轉換?

+2

UTF8可以代表65536個點; latin1(ISO-8859-1)只能表示256.你想如何處理所有無法轉換的字符? – simonc

+0

你可以轉換成C這個http://www.jamesmurty.com/2011/12/30/python-code-utf8-to-latin1/(注意不是所有的符號都可以轉換) –

+1

@DavidRF條件「不使用任何額外的庫「意味着不使用現成的函數,如在給定代碼的最後一行,'utf8_text.encode('ISO-8859-1','替換')' – Dialecticus

回答

4
typedef unsigned value_type; 

template <typename Iterator> 
size_t get_length (Iterator p) 
{ 
    unsigned char c = static_cast<unsigned char> (*p); 
    if (c < 0x80) return 1; 
    else if (!(c & 0x20)) return 2; 
    else if (!(c & 0x10)) return 3; 
    else if (!(c & 0x08)) return 4; 
    else if (!(c & 0x04)) return 5; 
    else return 6; 
} 

template <typename Iterator> 
value_type get_value (Iterator p) 
{ 
    size_t len = get_length (p); 

    if (len == 1) 
    return *p; 

    value_type res = static_cast<unsigned char> (
            *p & (0xff >> (len + 1))) 
            << ((len - 1) * 6); 

    for (--len; len; --len) 
     res |= (static_cast<unsigned char> (*(++p)) - 0x80) << ((len - 1) * 6); 

    return res; 
} 

該函數將返回位於p的unicode代碼點。現在,您可以轉換爲使用

for (std::string::iterator p = s_utf8.begin(); p != s_utf8.end(); ++p) 
{ 
    value_type value = get_value<std::string::iterator&>(p)); 
    if (value > 0xff) 
     throw "AAAAAH!"; 
    s_latin1.append(static_cast<char>(value)); 
} 

不能保證一個字符串,該代碼是很老:)

+0

是的,我知道UTF-8的官方只支持最大4字節的長度,這可以實施更迂迴。 – filmor

+0

這是否也能正確轉換德語元音變音(ö,ä,ü,ß)? – ashiaka

+0

@ashiaka:我懷疑它......我不認爲這些字符在latin1中可用... – Goz

-2

latin1(又名ISO-8859-1)定義了Unicode的前256個編碼點。因此,在UTF-8中,如果您的字符是8位,那麼它將精確映射到latin1等價物。如果長度超過8位,那麼在latin1內沒有通訊者,您應該將其映射到某個「未知字符」(例如,\0或?)。

+3

這是不正確的。它只適用於* 7 *位。 – filmor

+0

真的嗎?該死......在這種情況下,我猜測OP可以使用這個,然後手動映射剩下的128個點。 – Xophmeister

+0

從UTF-16到latin1的轉換隻是簡單地刪除每一個零,但是從UTF-8到latin1的轉換有點複雜。 – Dialecticus

1

這裏是filmor的答案,我寫我的目的的一個版本。更可讀,可能會慢一點。我不需要模板的東西,因爲我總是處理char *,在我的情況下,我想用_替換非Latin1字符。萬一它可以幫助別人:

int GetUtf8CharacterLength(unsigned char utf8Char) 
{ 
    if (utf8Char < 0x80) return 1; 
    else if ((utf8Char & 0x20) == 0) return 2; 
    else if ((utf8Char & 0x10) == 0) return 3; 
    else if ((utf8Char & 0x08) == 0) return 4; 
    else if ((utf8Char & 0x04) == 0) return 5; 

    return 6; 
} 

char Utf8ToLatin1Character(char *s, int *readIndex) 
{ 
    int len = GetUtf8CharacterLength(static_cast<unsigned char>(s[ *readIndex ])); 
    if (len == 1) 
    { 
     char c = s[ *readIndex ]; 
     (*readIndex)++; 

     return c; 
    } 

    unsigned int v = (s[ *readIndex ] & (0xff >> (len + 1))) << ((len - 1) * 6); 
    (*readIndex)++; 
    for (len-- ; len > 0 ; len--) 
    { 
     v |= (static_cast<unsigned char>(s[ *readIndex ]) - 0x80) << ((len - 1) * 6); 
     (*readIndex)++; 
    } 

    return (v > 0xff) ? 0 : (char)v; 
} 

// overwrites s in place 
char *Utf8ToLatin1String(char *s) 
{ 
    for (int readIndex = 0, writeIndex = 0 ; ; writeIndex++) 
    { 
     if (s[ readIndex ] == 0) 
     { 
      s[ writeIndex ] = 0; 
      break; 
     } 

     char c = Utf8ToLatin1Character(s, &readIndex); 
     if (c == 0) 
     { 
      c = '_'; 
     } 

     s[ writeIndex ] = c; 
    } 

    return s; 
} 

測試代碼:

char s2[ 256 ] = "lif\xc3\xa9 is b\xc3\xa9tt\xc3\xa9r with acc\xc3\xa9nts"; 
Utf8ToLatin1String(s2);