Project: finder, src: ucs4.c
UCS4 と UTF-8 の相互変換。
#include "ucs4.h"
#include <stdlib.h>
/**
* 与えれた UTF8 文字列から UCS4 文字列長を推定する
*/
static int ucs4LenFromUtf8(const char *utf8, int len8) {
unsigned char cc;
int shift = 0, len4 = 0;
while (len8 >= 0 && (cc = (unsigned char)(*utf8)) != '\0') {
if ((cc & 0x80) == 0) {
len4++; utf8 += 1; len8 -= 1;
} else if ((cc & 0xe0) == 0xc0) {
len4++; utf8 += 2; len8 -= 2;
} else if ((cc & 0xf0) == 0xe0) {
len4++; utf8 += 3; len8 -= 3;
} else if ((cc & 0xf8) == 0xf0) {
len4++; utf8 += 4; len8 -= 4;
} else if ((cc & 0xfc) == 0xf8) {
len4++; utf8 += 5; len8 -= 5;
} else if ((cc & 0xfe) == 0xfc) {
len4++; utf8 += 6; len8 -= 6;
} else {
--len8;
}
}
return len4;
}
/**
* 与えられた UCS4 文字列から UTF-8 文字列長を推定する
*/
static int utf8LenFromUcs4(const UCS4 *ucs4, int len) {
int len8 = 0;
while (len > 0) {
UCS4 cc = *(ucs4++);
--len;
if (cc < 0x80) {
len8 += 1;
} else if (cc < 0x800) {
len8 += 2;
} else if (cc < 0x10000) {
len8 += 3;
} else if (cc < 0x200000) {
len8 += 4;
} else if (cc < 0x4000000) {
len8 += 5;
} else {
len8 += 6;
}
}
return len8;
}
/**
* UCS4 文字列の長さを返す(キャラクタ単位)
*/
int ucs4Len(const UCS4 *ucs4) {
int len = 0;
if (ucs4 == NULL) {
return -1;
}
while (*(ucs4++) != 0) {
++len;
}
return len;
}
/**
* UTF8 文字列から UCS4 文字列への変換
*/
UCS4 *ucs4FromUtf8(const char *utf8, int len, int *plen4) {
int len4_target = ucs4LenFromUtf8(utf8, len);
UCS4 *ucs4 = (UCS4 *)malloc((len4_target + 1) * sizeof(UCS4));
UCS4 *p = ucs4;
while (len > 0) {
int cc = (int)*(utf8++) & 255;
--len;
if ((cc & 0x80) == 0) {
// 7bit 文字
*(p++) = cc;
} else if ((cc & 0xe0) == 0xc0) {
// 2byte 文字
if (len <= 1) break;
int cc2 = (int)*(utf8++);
--len;
*(p++) = ((cc & 0x1f) << 6) | (cc2 & 0x3f);
} else if ((cc & 0xf0) == 0xe0) {
// 3byte 文字
int cc2[2];
if (len < 2) break;
cc2[0] = (int)*(utf8++) & 255;
cc2[1] = (int)*(utf8++) & 255;
len -= 2;
*(p++) = ((cc & 0x0f) << 12) | ((cc2[0] & 0x3f) << 6) | (cc2[1] & 0x3f);
} else if ((cc & 0xf8) == 0xf0) {
// 4byte 文字
int cc2[3];
if (len < 3) break;
cc2[0] = (int)*(utf8++) & 255;
cc2[1] = (int)*(utf8++) & 255;
cc2[2] = (int)*(utf8++) & 255;
len -= 3;
*(p++) = ((cc & 0x07) << 18) | ((cc2[0] & 0x3f) << 12) | ((cc2[1] & 0x3f) << 6) | (cc2[2] & 0x3f);
} else if ((cc & 0xfc) == 0xf8) {
// 5byte 文字
int cc2[4];
if (len < 4) break;
cc2[0] = (int)*(utf8++) & 255;
cc2[1] = (int)*(utf8++) & 255;
cc2[2] = (int)*(utf8++) & 255;
cc2[3] = (int)*(utf8++) & 255;
len -= 4;
*(p++) = ((cc & 0x03) << 24) | ((cc2[0] & 0x3f) << 18) | ((cc2[1] & 0x3f) << 12) | ((cc2[2] & 0x3f) << 6) | (cc2[3] & 0x3f);
} else if ((cc & 0xfe) == 0xfc) {
int cc2[5];
if (len < 5) break;
cc2[0] = (int)*(utf8++) & 255;
cc2[1] = (int)*(utf8++) & 255;
cc2[2] = (int)*(utf8++) & 255;
cc2[3] = (int)*(utf8++) & 255;
cc2[4] = (int)*(utf8++) & 255;
len -= 5;
*(p++) = ((cc & 0x01) << 30) | ((cc2[0] & 0x3f) << 24) | ((cc2[1] & 0x3f) << 18) | ((cc2[2] & 0x3f) << 12) | ((cc2[3] & 0x3f) << 6) | (cc2[4] & 0x3f);
} else {
// UTF-8 文字列ではない
break;
}
}
*p = 0;
if (plen4) {
*plen4 = p - ucs4;
}
return ucs4;
}
/**
* UCS4 文字列から UTF8 文字列への変換
*/
char *utf8FromUCS4(const UCS4 *ucs4, int len, int *plen8) {
int len8 = utf8LenFromUcs4(ucs4, len);
char *utf8 = (char *)malloc(len8 + 1), *p = utf8;
while (len > 0) {
UCS4 cc = *(ucs4++);
--len;
if (cc < 0x80) {
*(p++) = cc;
} else if (cc < 0x800) {
*(p++) = 0xc0 | (cc >> 6);
*(p++) = 0x80 | (cc & 0x3f);
} else if (cc < 0x10000) {
*(p++) = 0xe0 | (cc >> 12);
*(p++) = 0x80 | ((cc >> 6) & 0x3f);
*(p++) = 0x80 | (cc & 0x3f);
} else if (cc < 0x200000) {
*(p++) = 0xf0 | (cc >> 18);
*(p++) = 0x80 | ((cc >> 12) & 0x3f);
*(p++) = 0x80 | ((cc >> 6) & 0x3f);
*(p++) = 0x80 | (cc & 0x3f);
} else if (cc < 0x4000000) {
*(p++) = 0xf8 | (cc >> 24);
*(p++) = 0x80 | ((cc >> 18) & 0x3f);
*(p++) = 0x80 | ((cc >> 12) & 0x3f);
*(p++) = 0x80 | ((cc >> 6) & 0x3f);
*(p++) = 0x80 | (cc & 0x3f);
} else {
*(p++) = 0xfc | (cc >> 30);
*(p++) = 0x80 | ((cc >> 24) & 0x3f);
*(p++) = 0x80 | ((cc >> 18) & 0x3f);
*(p++) = 0x80 | ((cc >> 12) & 0x3f);
*(p++) = 0x80 | ((cc >> 6) & 0x3f);
*(p++) = 0x80 | (cc & 0x3f);
}
}
if (plen8) {
*plen8 = len8;
}
return utf8;
}
#ifdef DEBUG
#include <stdio.h>
#include <string.h>
int main(int argc, char **argv) {
UCS4 *ucs4;
char *utf8;
int len4, len8;
if (argc < 2) {
printf("usage: %s <string to be translated>\n", argv[0]);
exit(1);
}
// UCS4 に変換
ucs4 = ucs4FromUtf8(argv[1], strlen(argv[1]), &len4);
// UTF-8 に再変換
utf8 = utf8FromUCS4(ucs4, len4, &len8);
printf("re-translation: %.*s\n", len8, utf8);
free(ucs4);
free(utf8);
return 0;
}
#endif // DEBUG