2012-06-22 23 views
7

私のソフトウェアは、ISO 8859に変換する必要があるよりも、UTF8でいくつかの文字列を取得しています。私はUTF8ドメインがiso 8859よりも大きいことを知っています。しかし、UTF8のデータはISOから以前はアップコンバートされていました。UTF8からiso-8859-1に変換する方法はありますか?

UTF8からiso-8859-1に簡単に変換できる方法があるかどうかを知りたいと思います。

おかげ

+1

変換を行ったライブラリを使用している場合は、変換するものもあるはずです。あなたが文字列内の文字を変更しなかったと仮定すると、ただそれを返すだけでいいはずです。 – RedX

答えて

11

はあなたが便利かもしれない機能です。 ISO-8859-15(EURO、ISO-8859-1はありません)に変換されますが、ISO-8859-1 - >UTF-8 - >ISO-8859-1往復の変換部のUTF-8 - >ISO-8859-1変換部についても正しく動作します。

この関数は、iconvの//IGNOREフラグに似た無効なコードポイントを無視しますが、分解されたUTF-8シーケンスは再構成しません。つまり、U+006E U+0303U+00F1になりません。私はiconvもそうでないので再構成を気にしない。

機能は、文字列アクセスに非常に注意しています。それはバッファを越えてスキャンされることはありません。出力バッファは、常にend-of-string NULバイトを付加するため、lengthより1バイト長くする必要があります。この関数は、出力の文字数(バイト)を返します。文字列の終わりのNULバイトは含みません。あなたはto_latin9()機能で特定のコード・ポイント用のカスタム音訳を追加することができますが、1文字の代替品に限定されている

/* 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は、UTF-8変換(ISO-8859-1または-15/UTF-8から)からLatin-1の双方/ 9 /への変換関数のペアin an edit to this answerを含ま。主な違いは、これらの関数が動的に割り当てられたコピーを返し、元の文字列をそのまま維持することです。

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"です。

の作業例:utf8_to_latin9():ここ

#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