2012-06-22 134 views
7

我的軟件在UTF8中獲取的字符串比我需要轉換爲ISO 8859 1.我知道UTF8域大於iso 8859.但UTF8中的數據先前已從ISO上轉換,因此我不應該錯過任何內容。有沒有辦法將UTF8轉換爲iso-8859-1?

我想知道是否有一種簡單/直接的方式將UTF8轉換爲iso-8859-1。

由於

+1

如果您使用的是進行了轉換的庫,則還應該將其轉換回來。假設你沒有改變字符串中的任何字符,你應該罰款回來。 – RedX

回答

11

這裏是你可能會發現有用的功能:utf8_to_latin9()。它會轉換爲ISO-8859-15(包括歐元,其中ISO-8859-1不具備),但也可以正確地用於UTF-8ISO-8859-1轉換部分的ISO-8859-1UTF-8ISO-8859-1往返。

該函數忽略與iconv的//IGNORE標誌類似的無效代碼點,但不重構分解的UTF-8序列;也就是說,它不會將U+006E U+0303轉換爲U+00F1。我不打擾重組,因爲iconv不會。

該函數對字符串訪問非常小心。它永遠不會掃描超出緩衝區。輸出緩衝區的長度必須比字符長一個字節,因爲它總是附加字符串尾部的NUL字節。該函數返回輸出中的字符數(字節數),不包括字符串結尾的NUL字節。

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

注意,您可以在to_latin9()功能添加自定義音譯爲特定的代碼點,但僅限於一個字符替換。

正如它目前所寫,該函數可以安全地進行就地轉換:輸入和輸出指針可以是相同的。輸出字符串永遠不會比輸入字符串長。如果您的輸入字符串有額外字節的空間(例如,它有NUL終止字符串),您可以安全地使用上述函數將其從UTF-8轉換爲ISO-8859-1/15。我故意以這種方式編寫它,因爲它可以爲嵌入式環境節省一些工作量,儘管這種方法有點受限。定製和擴展。

編輯:

我包括用於對轉換函數in an edit to this answer拉丁文-1/9 /從UTF-8轉化率(ISO-8859-1或-15至/從UTF-8);主要區別是這些函數返回一個動態分配的副本,並保持原始字符串不變。

10

的iconv - 執行字符集轉換

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"

工作例如:

#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

謝謝,我忘了指出的主要問題是我的軟件在嵌入式Linux上運行,而iconv不可用。 – fazineroso

+0

你可以爲你的linux編譯iconv。你的linux使用glibc嗎?如果是的話,它有它的兼容實現,名爲'gconv':http://www.gnu.org/software/libc/manual/html_node/glibc-iconv-Implementation.html –

+0

@fazineroso有沒有使用庫的解決方案調用。我現在要走了,但如果明天左右沒有更好的話,我會更新我的答案。 – kay

相關問題