2012-10-12 3 views
5

내가 가진 문제는 매우 간단하지만, 내가 지금까지 해결책을 찾을 수 없습니다 :C/C++에서 UTF8에서 Latin1로 문자열을 변환하는 방법은 무엇입니까?

가 어떻게이 UTF8이 libiconv 같은 여분의 libs와 사용하지 않고 C++로 string을 인코딩 라틴에 string 인코딩 변환 할 수 있습니까?

지금까지 latin1에서 UTF8로 변환하는 모든 예제가 있습니다.

+2

UTF8 65536 코드 포인트를 나타낼 수 있습니다; latin1 (ISO-8859-1)은 256 밖에 표현할 수 없습니다. 변환 할 수없는 모든 문자를 어떻게 처리하고 싶습니까? – simonc

+0

당신은 C this http://www.jamesmurty.com/2011/12/30/python-code-utf8-to-latin1/로 번역 할 수 있습니다 (모든 기호를 변환 할 수있는 것은 아닙니다). –

+1

@DavidRF 조건 "사용하지 않고 모든 여분의 libs "는 주어진 코드의 마지막 줄과 같이 준비된 함수를 사용하지 않는다는 것을 의미합니다. 'utf8_text.encode ('ISO-8859-1 ','replace ')' – 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으로 반환합니다. 이제

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 (aka ISO-8859-1)은 유니 코드의 처음 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); 
관련 문제