2012-10-12 72 views
5

Câu hỏi tôi có là khá đơn giản, nhưng tôi không thể tìm thấy một giải pháp cho đến nay:Làm thế nào để chuyển đổi một chuỗi từ UTF8 sang Latin1 trong C/C++?

Làm thế nào tôi có thể chuyển đổi một UTF8 mã hóa string đến một latin1 mã hóa string trong C++ mà không sử dụng bất kỳ libs thêm như libiconv?

Mỗi ví dụ tôi có thể tìm thấy cho đến nay là để chuyển đổi latin1 sang UTF8?

+2

UTF8 thể đại diện cho 65.536 điểm mã; latin1 (ISO-8859-1) chỉ có thể đại diện cho 256. Bạn muốn xử lý tất cả các ký tự không thể chuyển đổi như thế nào? – simonc

+0

Bạn có thể dịch sang C này http://www.jamesmurty.com/2011/12/30/python-code-utf8-to-latin1/ (lưu ý rằng không phải tất cả các ký hiệu đều có thể được chuyển đổi) –

+1

@DavidRF condition "mà không sử dụng bất kỳ libs bổ sung nào "có nghĩa là không sử dụng các hàm sẵn sàng như trong dòng cuối cùng của mã đã cho,' utf8_text.encode ('ISO-8859-1', 'thay thế') ' – Dialecticus

Trả lời

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

Chức năng này sẽ trả về điểm mã unicode tại p. Bây giờ bạn có thể chuyển đổi một chuỗi sử dụng

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

Không bảo lãnh, mã khá cũ :)

+0

Và có, tôi biết rằng UTF-8 chính thức chỉ hỗ trợ độ dài tối đa 4 byte, điều này có thể được triển khai nhiều hơn. – filmor

+0

Điều này có chuyển đổi các ngôn ngữ tiếng Đức (ö, ä, ü, ß) một cách chính xác không? – ashiaka

+0

@ashiaka: Tôi nghi ngờ nó ... Tôi không nghĩ rằng những nhân vật có sẵn trong latin1 ... – Goz

-2

latin1 (aka ISO-8859-1) xác định 256 điểm mã đầu tiên của Unicode. Như vậy, trong UTF-8, nếu ký tự của bạn là 8 bit, thì nó sẽ chính xác ánh xạ tới tương đương latin1. Nếu nó dài hơn 8 bit, thì không có phóng viên nào trong vòng latin1 và bạn nên ánh xạ nó tới một số "ký tự không xác định" (ví dụ: \0 hoặc?).

+3

Điều này không đúng. Nó chỉ hoạt động như thế này cho * 7 * bit. – filmor

+0

Thật sao? Chết tiệt ... Trong trường hợp đó, tôi đoán OP có thể sử dụng điều này và sau đó tự ánh xạ 128 điểm còn lại. – Xophmeister

+0

Chuyển đổi từ UTF-16 sang latin1 đơn giản là xóa mọi số 0, nhưng chuyển đổi từ UTF-8 sang latin1 hơi phức tạp. – Dialecticus

1

Đây là một phiên bản của câu trả lời filmor rằng tôi đã viết cho mục đích của tôi. Có thể đọc được một chút, có thể chậm hơn một chút. Tôi không cần các công cụ mẫu vì tôi luôn giao dịch với char * và trong trường hợp của tôi, tôi muốn thay thế ký tự không phải của Latin1 bằng _. Chỉ trong trường hợp nó giúp ai đó:

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

mã kiểm tra:

char s2[ 256 ] = "lif\xc3\xa9 is b\xc3\xa9tt\xc3\xa9r with acc\xc3\xa9nts"; 
Utf8ToLatin1String(s2); 
Các vấn đề liên quan