2012-10-12 18 views
5

Ben bir soru oldukça basit, ama şimdiye kadar bir çözüm bulamadık:Bir dize UTF8'den Latin1'e C/C++ nasıl dönüştürülür?

nasıl UTF8 libiconv gibi herhangi bir ekstra kütüphanelerini kullanmadan C++ string kodlanan bir Latin1 için string kodlanmış dönüştürebilirsiniz?

Şu ana kadar bulabildiğim her şey, latin1 ile UTF8 dönüşümü arasında mı?

+2

UTF8 65536 kod noktalarını temsil edebilir; latin1 (ISO-8859-1) sadece 256'yı temsil edebilir. Dönüştürülemeyen tüm karakterlerle nasıl ilgilenmek istersiniz? – simonc

+0

C'ye bu http://www.jamesmurty.com/2011/12/30/python-code-utf8-to-latin1/ (tüm sembollerin dönüştürülemeyeceğini unutmayın) –

+1

@DavidRF koşulu " herhangi bir ekstra libs "verilen kodun son satırında olduğu gibi hazır fonksiyonların kullanılmaması anlamına gelir," utf8_text.encode ('ISO-8859-1', 'replace') ' – Dialecticus

cevap

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; 
} 

Bu işlev, unicode kod noktasını p adresinde döndürecektir. Artık

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)); 
} 

yok garantileri kullanarak bir dize dönüştürebilirsiniz, kod oldukça eskidir :)

İşte
+0

içinde çok bayt kodlama kodunuz var. Ve evet, UTF-8'in resmi olarak yalnızca maksimum 4 bayt uzunluğunu desteklediğini biliyorum, bu daha fazla bilgi akışına uygulanabilir. – filmor

+0

Bu, Alman umlautsını (ö, ä, ü, ß) doğru olarak dönüştürüyor mu? – ashiaka

+0

@ashiaka: Şüphem ... Bu karakterlerin latin1'de mevcut olduğunu sanmıyorum1 ... – Goz

-2

latin1 (aka ISO-8859-1) Unicode ilk 256 kod noktalarını tanımlar. Böylece, UTF-8, karakteriniz 8 bit ise, o zaman tam olarak latin1 eşdeğerini eşler. Eğer uzunluk 8 bitten fazla ise, o zaman latin1 içerisinde bir muhabir yoktur ve onu bir "bilinmeyen karakter" ile eşleştirmelisiniz (ör. \0 veya?).

+3

Bu doğru değil. Sadece 7 * bit için böyle çalışır. – filmor

+0

Gerçekten mi? Lanet olsun ... Bu durumda, OP bunu kullanabilir ve kalan 128 noktayı el ile eşleştirebilir. – Xophmeister

+0

UTF-16'dan latin1'e dönüştürme, her sıfırı sıfırdan kaldırır, ancak UTF-8'den latin1'e dönüştürme biraz karmaşıktır. – Dialecticus

1

benim amaçlar için yazdığı filmor en cevabın bir versiyonudur. Biraz daha okunabilir, muhtemelen biraz daha yavaş. Ben her zaman char * ile uğraştığım için şablon şeyler gerek yoktu ve benim durumumda ben ile Latin olmayan karakterleri değiştirmek istedim _. Her ihtimale karşı yardımcı olur birisi:

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; 
} 

Testi kodu:

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