2012-06-22 17 views
7

Yazılımım, UTF8'de ISO 8859'a dönüştürmem gerektiğinden daha fazla ip alıyor. 1. UTF8 etki alanının ISO 8859'dan daha büyük olduğunu biliyorum. Ancak UTF8'deki veriler daha önce ISO'dan dönüştürülmüş durumda, bu yüzden hiçbir şey kaçırmamalıyım.UTF8'den iso-8859-1'e dönüştürmenin bir yolu var mı?

UTF8'den iso-8859-1'e dönüştürmenin kolay/doğrudan bir yolu olup olmadığını bilmek istiyorum.

Teşekkür

+1

Dönüştürmeyi yapan bir kitaplık kullanıyorsanız, onu dönüştürmek için de bir şey olmalıdır. Dizede herhangi bir karakter değiştirmediğinizi varsayarak, sadece geri vermeniz gerekir. – RedX

cevap

11

yararlı bulabileceğiniz bir işlevdir: utf8_to_latin9(). ISO-8859-15 (ISO-8859-1'un sahip olmadığı EURO dahil), ancak UTF-8 ->ISO-8859-1 -> ->UTF-8 ->ISO-8859-1 gidiş dönüşü için doğru şekilde çalışıyor. Bu işlev, iconv için //IGNORE bayrağına benzer geçersiz kod noktalarını yoksayar, ancak ayrışmış UTF-8 dizilerini yeniden oluşturmaz; Yani, U+006E U+0303U+00F1'a dönmez. İkonu da rahatsız etmiyorum çünkü ikona da sahip değil.

İşlev, dizi erişimi hakkında çok dikkatlidir. Tamponun ötesini asla taramayacaktır. Çıkış arabelleği her zaman uzunluktan daha uzun bir bayt olmalıdır, çünkü her zaman dizenin sonu NUL baytını ekler. İşlev, dize sonu NUL baytını içermeyen, çıktıdaki karakter sayısını (bayt) döndürür. Eğer to_latin9() fonksiyonunda belirli kod noktaları için özel çevirisini ekleyebilirsiniz, ancak tek karakterlik değiştirmeler ile sınırlıdır

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

Not.

Şu anda yazıldığı gibi, işlev yerinde dönüşümü güvenle yapabilir: giriş ve çıkış işaretçileri aynı olabilir. Çıkış dizesi asla giriş dizesinden uzun olmayacaktır. Giriş dizenizde ek bayt için yer varsa (örneğin, dizeyi sonlandıran NUL vardır), yukarıdaki işlevi UTF-8'den ISO-8859-1/15'e dönüştürmek için güvenle kullanabilirsiniz. Bu şekilde kasıtlı olarak yazdım, çünkü bu yaklaşım biraz sınırlı olsa da, gömülü bir ortamda size biraz çaba sarfetmelidir. özelleştirme ve genişletme.

Düzenleme: dönüşüm işlevleri in an edit to this answer bir çift dahil

hem Latince-1/9 kadar/UTF-8 çevrimden (ISO-8859-1 veya -15 ila/UTF-8); temel fark, bu işlevlerin dinamik olarak ayrılmış bir kopyasını döndürmesi ve orijinal dizeyi bozulmamış halde tutmasıdır.

10

iconv - karakter kümesi dönüşüm için

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

tocode"ISO_8859-1" ve fromcode olduğunu "UTF-8" olduğunu

size_t iconv(iconv_t cd, char **inbuf, size_t *inbytesleft, char **outbuf, size_t *outbytesleft); gerçekleştirin.

Çalışma örneği: Burada

#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

Teşekkürler, sahip olduğum ve belirtmeyi unuttum ana sorun, yazılımımın yerleşik Linux üzerinde çalıştığını ve iconv'un mevcut olmadığını gösteriyor. – fazineroso

+0

Linux'unuz için iconv derleyebilirsiniz. Linux'unuz glibc kullanıyor mu? Evet ise, gconv olarak adlandırılan uyumlu bir uygulaması vardır: http://www.gnu.org/software/libc/manual/html_node/glibc-iconv-Implementation.html –

+0

@fazineroso kütüphane kullanmayan çözümler var. çağırır. Şimdi gitmeliyim, ama yarın daha iyi bir şey elde edemezsen cevabımı güncellerim. – kay

İlgili konular