utf16.c

UTF-8は、よく使われるものは小さいバイトで、そうでないものは大きなバイト(例えば日本語は3バイト)で表される符号化方法ですが、
UTF-16は、全て2バイト(でも文字が増えてしまって4バイトもたまにある)で符号化しています。
まあなんとなくしか理解していませんが、だいたいそんな感じです。

というわけで、UTF-16をUTF-8に変換するu16_u16tou8関数を作りました。(ほとんどどこかからのコピペなのですが…(汗))
#ifndef _UTF16
#define _UTF16

// u16(2B(4B)) to u8(1~3B)
// read by 2B
// return byte of dst
int u16_u16tou8(unsigned char *dst, unsigned char *src){
   static unsigned long salo_up = 0;
   unsigned long n;
   n = src[0] * 0x100 + src[1];
   // n : aaaa aaaa bbbb bbbb
   if(n <= 0x7f){  // ASCII compatible
      dst[0] = src[1];
      return 1;
   }
   if(n <= 0x7ff){
      // 8421 8421   8421 8421
      // 110x_xxxx | 10xx_xxxx
      dst[0] = 0xc0 | (n >> 6);
      dst[1] = 0x80 | (n & 0x3f);
      return 2;
   }
   if(0xd800 <= n && n <= 0xdbff){
      printf("[s0]");
      salo_up = n;
      return 0;
   }
   if(0xdc00 <= n && n <= 0xdfff){
      printf("[s1]");
      // n<=0x1fffff
      // 8421 8421   8421 8421   8421 8421   8421 8421
      // 1111_0xxx | 10xx_xxxx | 10xx xxxx | 10xx xxxx
      n = (salo_up - 0xd800) * 0x400 + (n - 0xdc00) + 0x10000;
      dst[0] = 0xf0 | ((n >> 18) & 0x07);
      dst[1] = 0x80 | ((n >> 12) & 0x3f);
      dst[2] = 0x80 | ((n >> 6) & 0x3f);
      dst[3] = 0x80 | (n & 0x3f);
      return 4;
   }
   // 8421 8421   8421 8421   8421 8421
   // 1110_xxxx | 10xx_xxxx | 10xx_xxxx
   dst[0] = 0xe0 | ((n >> 12) & 0x0f);
   dst[1] = 0x80 | ((n >> 6) & 0x3f);
   dst[2] = 0x80 | (n & 0x3f);
   return 3;

   // there is defined more, but not used.
}


#endif
はい。
Wikipediaにある通り、Unicode(UTF-16)からの変換を定義にのっとってやった感じです。
ちとややこしいですが、定義だからしょうがないです。

ごくまれにある、4バイトを4バイトに変換する部分は、先に2バイト来た時にそれを保存しておき、次の2バイトで一気に処理するようにしていますが、なんだかできてる気がしません。
とりあえずそんなことがあったら[s0][s1]と表示するようにしています。