2012-06-22 7 views
7

내 소프트웨어가 ISO 8859 1로 변환해야하는 것보다 UTF8에서 일부 문자열이 나타납니다. UTF8 도메인이 iso 8859보다 큽니다. UTF8의 데이터는 이전에 ISO에서 상향 변환되었으므로 아무 것도 놓치지 마십시오.UTF8에서 iso-8859-1로 변환하는 방법이 있습니까?

저는 UTF8에서 iso-8859-1로 변환하는 쉬운/직접적인 방법이 있는지 알고 싶습니다.

감사

+1

변환을 수행 한 라이브러리를 사용하는 경우 변환 할 대상이 있어야합니다. 문자열의 문자를 변경하지 않았다고 가정하면 다시 돌려 주면됩니다. – RedX

답변

11

당신이 유용 할 수있는 기능입니다 : utf8_to_latin9(). ISO-8859-15 (EURO 포함, ISO-8859-1은 포함되지 않음)으로 변환되지만 ISO-8859-1 ->UTF-8 ->ISO-8859-1 왕복의 ->ISO-8859-1 변환 부분에 대해서도 올바르게 작동합니다.

이 함수는 iconv에 //IGNORE 플래그와 비슷한 잘못된 코드 포인트를 무시하지만 분해 된 UTF-8 시퀀스는 재구성하지 않습니다. 즉 U+006E U+0303U+00F1이되지 않습니다. 나는 iconv도하지 않기 때문에 재구성하는 것을 신경 쓰지 않는다.

이 함수는 문자열 액세스에 매우주의해야합니다. 그것은 버퍼를 넘어 스캔하지 않습니다. 출력 버퍼는 항상 end-of-string NUL 바이트를 추가하기 때문에 길이보다 1 바이트 길어야합니다. 이 함수는 문자열의 끝 NUL 바이트를 제외하고 출력의 문자 수 (바이트)를 반환합니다. 당신이 to_latin9() 기능의 특정 코드 포인트에 대한 사용자 정의 음역을 추가 할 수 있지만 하나의 문자 교체로 제한됩니다

/* UTF-8 to ISO-8859-1/ISO-8859-15 mapper. 
* Return 0..255 for valid ISO-8859-15 code points, 256 otherwise. 
*/ 
static inline unsigned int to_latin9(const unsigned int code) 
{ 
    /* Code points 0 to U+00FF are the same in both. */ 
    if (code < 256U) 
     return code; 
    switch (code) { 
    case 0x0152U: return 188U; /* U+0152 = 0xBC: OE ligature */ 
    case 0x0153U: return 189U; /* U+0153 = 0xBD: oe ligature */ 
    case 0x0160U: return 166U; /* U+0160 = 0xA6: S with caron */ 
    case 0x0161U: return 168U; /* U+0161 = 0xA8: s with caron */ 
    case 0x0178U: return 190U; /* U+0178 = 0xBE: Y with diaresis */ 
    case 0x017DU: return 180U; /* U+017D = 0xB4: Z with caron */ 
    case 0x017EU: return 184U; /* U+017E = 0xB8: z with caron */ 
    case 0x20ACU: return 164U; /* U+20AC = 0xA4: Euro */ 
    default:  return 256U; 
    } 
} 

/* Convert an UTF-8 string to ISO-8859-15. 
* All invalid sequences are ignored. 
* Note: output == input is allowed, 
* but input < output < input + length 
* is not. 
* Output has to have room for (length+1) chars, including the trailing NUL byte. 
*/ 
size_t utf8_to_latin9(char *const output, const char *const input, const size_t length) 
{ 
    unsigned char    *out = (unsigned char *)output; 
    const unsigned char  *in = (const unsigned char *)input; 
    const unsigned char *const end = (const unsigned char *)input + length; 
    unsigned int    c; 

    while (in < end) 
     if (*in < 128) 
      *(out++) = *(in++); /* Valid codepoint */ 
     else 
     if (*in < 192) 
      in++;    /* 10000000 .. 10111111 are invalid */ 
     else 
     if (*in < 224) {  /* 110xxxxx 10xxxxxx */ 
      if (in + 1 >= end) 
       break; 
      if ((in[1] & 192U) == 128U) { 
       c = to_latin9((((unsigned int)(in[0] & 0x1FU)) << 6U) 
          | ((unsigned int)(in[1] & 0x3FU))); 
       if (c < 256) 
        *(out++) = c; 
      } 
      in += 2; 

     } else 
     if (*in < 240) {  /* 1110xxxx 10xxxxxx 10xxxxxx */ 
      if (in + 2 >= end) 
       break; 
      if ((in[1] & 192U) == 128U && 
       (in[2] & 192U) == 128U) { 
       c = to_latin9((((unsigned int)(in[0] & 0x0FU)) << 12U) 
          | (((unsigned int)(in[1] & 0x3FU)) << 6U) 
          | ((unsigned int)(in[2] & 0x3FU))); 
       if (c < 256) 
        *(out++) = c; 
      } 
      in += 3; 

     } else 
     if (*in < 248) {  /* 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx */ 
      if (in + 3 >= end) 
       break; 
      if ((in[1] & 192U) == 128U && 
       (in[2] & 192U) == 128U && 
       (in[3] & 192U) == 128U) { 
       c = to_latin9((((unsigned int)(in[0] & 0x07U)) << 18U) 
          | (((unsigned int)(in[1] & 0x3FU)) << 12U) 
          | (((unsigned int)(in[2] & 0x3FU)) << 6U) 
          | ((unsigned int)(in[3] & 0x3FU))); 
       if (c < 256) 
        *(out++) = c; 
      } 
      in += 4; 

     } else 
     if (*in < 252) {  /* 111110xx 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx */ 
      if (in + 4 >= end) 
       break; 
      if ((in[1] & 192U) == 128U && 
       (in[2] & 192U) == 128U && 
       (in[3] & 192U) == 128U && 
       (in[4] & 192U) == 128U) { 
       c = to_latin9((((unsigned int)(in[0] & 0x03U)) << 24U) 
          | (((unsigned int)(in[1] & 0x3FU)) << 18U) 
          | (((unsigned int)(in[2] & 0x3FU)) << 12U) 
          | (((unsigned int)(in[3] & 0x3FU)) << 6U) 
          | ((unsigned int)(in[4] & 0x3FU))); 
       if (c < 256) 
        *(out++) = c; 
      } 
      in += 5; 

     } else 
     if (*in < 254) {  /* 1111110x 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx */ 
      if (in + 5 >= end) 
       break; 
      if ((in[1] & 192U) == 128U && 
       (in[2] & 192U) == 128U && 
       (in[3] & 192U) == 128U && 
       (in[4] & 192U) == 128U && 
       (in[5] & 192U) == 128U) { 
       c = to_latin9((((unsigned int)(in[0] & 0x01U)) << 30U) 
          | (((unsigned int)(in[1] & 0x3FU)) << 24U) 
          | (((unsigned int)(in[2] & 0x3FU)) << 18U) 
          | (((unsigned int)(in[3] & 0x3FU)) << 12U) 
          | (((unsigned int)(in[4] & 0x3FU)) << 6U) 
          | ((unsigned int)(in[5] & 0x3FU))); 
       if (c < 256) 
        *(out++) = c; 
      } 
      in += 6; 

     } else 
      in++;    /* 11111110 and 11111111 are invalid */ 

    /* Terminate the output string. */ 
    *out = '\0'; 

    return (size_t)(out - (unsigned char *)output); 
} 

참고.

현재 작성되었으므로이 함수는 내부 변환을 안전하게 수행 할 수 있습니다. 입력 포인터와 출력 포인터는 동일 할 수 있습니다. 출력 문자열은 입력 문자열보다 길지 않습니다. 입력 문자열에 추가 바이트 (예 : 문자열을 종료하는 NUL이 있음)가있을 경우 위의 함수를 사용하여 UTF-8에서 ISO-8859-1/15로 안전하게 변환 할 수 있습니다. 이 방법은 약간 제한적이지만, 임베디드 환경에서 약간의 노력을 덜어주기 때문에 의도적으로이 방법으로 썼습니다. 사용자 정의 및 확장.

편집 : I가 변환 함수 in an edit to this answer의 쌍을 포함

모두 라틴어-1/9/UTF-8에서의 변환 (ISO-8859-1 또는 -15 내지/UTF-8); 가장 큰 차이점은 해당 함수가 동적으로 할당 된 복사본을 반환하고 원래 문자열을 그대로 유지한다는 것입니다.

10

iconv에서 - 문자 집합 변환을

iconv_t iconv_open(const char *tocode, const char *fromcode);

tocode"ISO_8859-1"fromcode입니다 "UTF-8"이다

size_t iconv(iconv_t cd, char **inbuf, size_t *inbytesleft, char **outbuf, size_t *outbytesleft);을 수행합니다.

근무 예 : 여기

#include <iconv.h> 
#include <stdio.h> 

int main (void) { 
    iconv_t cd = iconv_open("ISO_8859-1", "UTF-8"); 
    if (cd == (iconv_t) -1) { 
     perror("iconv_open failed!"); 
     return 1; 
    } 

    char input[] = "Test äöü"; 
    char *in_buf = &input[0]; 
    size_t in_left = sizeof(input) - 1; 

    char output[32]; 
    char *out_buf = &output[0]; 
    size_t out_left = sizeof(output) - 1; 

    do { 
     if (iconv(cd, &in_buf, &in_left, &out_buf, &out_left) == (size_t) -1) { 
      perror("iconv failed!"); 
      return 1; 
     } 
    } while (in_left > 0 && out_left > 0); 
    *out_buf = 0; 

    iconv_close(cd); 

    printf("%s -> %s\n", input, output); 
    return 0; 
} 
+0

고마워, 내가 가지고있는 주된 문제는 잊어 버리고 내 소프트웨어가 임베디드 리눅스에서 작동하고 iconv를 사용할 수 없다는 점을 잊어 버렸다. – fazineroso

+0

리눅스 용 iconv를 컴파일 할 수 있습니다. 당신의 리눅스는 glibc를 사용합니까? 그렇다면 호환 가능한 구현체 인'gconv'가 있습니다 : http://www.gnu.org/software/libc/manual/html_node/glibc-iconv-Implementation.html –

+0

@fazineroso 라이브러리를 사용하지 않는 솔루션이 있습니다 전화. 지금 가야 겠지만, 내일이면 더 좋은 결과를 얻지 못하면 답을 업데이트 할 것입니다. – kay

관련 문제