2012-06-22 67 views
7

Phần mềm của tôi nhận được một số chuỗi trong UTF8 hơn tôi cần chuyển đổi thành ISO 8859 1. Tôi biết rằng miền UTF8 lớn hơn ISO 8859. Nhưng dữ liệu trong UTF8 trước đây đã được chuyển đổi từ ISO, vì vậy tôi không nên bỏ lỡ bất kỳ điều gì.Có cách nào để chuyển đổi từ UTF8 sang iso-8859-1 không?

Tôi muốn biết liệu có cách dễ dàng/trực tiếp để chuyển đổi từ UTF8 sang iso-8859-1 hay không.

Cảm ơn

+1

Nếu bạn đang sử dụng thư viện đã thực hiện chuyển đổi, nó cũng phải có thứ gì đó để chuyển đổi lại. Giả sử bạn không thay đổi bất kỳ ký tự nào trong chuỗi, bạn sẽ ổn khi chỉ trả lại. – RedX

Trả lời

11

Đây là một chức năng bạn có thể thấy hữu ích: utf8_to_latin9(). Nó chuyển đổi thành ISO-8859-15 (bao gồm EURO, mà ISO-8859-1 không có), nhưng cũng hoạt động chính xác cho phần chuyển đổi UTF-8 ->ISO-8859-1 của ISO-8859-1 ->UTF-8 ->ISO-8859-1 khứ hồi.

Hàm bỏ qua các điểm mã không hợp lệ tương tự như //IGNORE cờ cho biểu tượngv, nhưng không phân phối lại chuỗi UTF-8 bị phân hủy; nghĩa là, nó sẽ không biến U+006E U+0303 thành U+00F1. Tôi không bận tâm đến việc phân loại lại vì iconv cũng vậy.

Chức năng này rất cẩn thận về truy cập chuỗi. Nó sẽ không bao giờ quét ngoài bộ đệm. Bộ đệm đầu ra phải dài hơn một byte so với độ dài, bởi vì nó luôn nối thêm byte NUL cuối chuỗi. Hàm trả về số ký tự (byte) trong đầu ra, không bao gồm byte NUL kết thúc chuỗi.

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

Lưu ý rằng bạn có thể thêm phiên âm tùy chỉnh cho điểm mã cụ thể trong to_latin9() chức năng, nhưng bạn được giới hạn thay thế một ký tự.

Vì nó hiện đang được viết, chức năng có thể thực hiện chuyển đổi tại chỗ một cách an toàn: các con trỏ đầu vào và đầu ra có thể giống nhau. Chuỗi đầu ra sẽ không bao giờ dài hơn chuỗi đầu vào. Nếu chuỗi đầu vào của bạn có chỗ cho một byte thừa (ví dụ, nó có NUL chấm dứt chuỗi), bạn có thể sử dụng một cách an toàn hàm trên để chuyển đổi nó từ UTF-8 thành ISO-8859-1/15. Tôi cố tình viết nó theo cách này, bởi vì nó sẽ giúp bạn tiết kiệm một số nỗ lực trong một môi trường nhúng, mặc dù cách tiếp cận này là một chút hạn chế wrt. tùy chỉnh và tiện ích.

Edit:

tôi bao gồm một cặp các chức năng chuyển đổi in an edit to this answer cho cả Latin-1/9 đến/từ UTF-8 chuyển đổi (ISO-8859-1 hoặc -15 đến/từ UTF-8); sự khác biệt chính là các hàm đó trả về một bản sao được phân bổ động và giữ nguyên chuỗi ban đầu.

10

iconv - thực hiện bộ ký tự chuyển đổi

size_t iconv(iconv_t cd, char **inbuf, size_t *inbytesleft, char **outbuf, size_t *outbytesleft);

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

tocode"ISO_8859-1"fromcode"UTF-8".

dụ làm việc:

#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

Cảm ơn, vấn đề chính của tôi và tôi quên chỉ định là phần mềm của tôi chạy trên Linux và biểu tượng nhúng không có sẵn. – fazineroso

+0

Bạn có thể biên dịch iconv cho Linux của mình. Linux của bạn có sử dụng glibc không? Nếu có, nó có triển khai tương thích của nó được gọi là 'gconv': http://www.gnu.org/software/libc/manual/html_node/glibc-iconv-Implementation.html –

+0

@fazineroso có các giải pháp không sử dụng thư viện cuộc gọi. Tôi phải đi ngay bây giờ, nhưng tôi sẽ cập nhật câu trả lời của tôi nếu bạn không có được một tốt hơn vào ngày mai hoặc lâu hơn. – kay

Các vấn đề liên quan