转换为UTF-32 很简单,只是Unicode code point。
#include <wchar.h>
wint_t codepoint_to_utf32( const wint_t codepoint ) {
if( codepoint > 0x10FFFF ) {
fprintf( stderr, "Codepoint %x is out of UTF-32 range\n", codepoint);
return -1;
}
return codepoint;
}
请注意,我使用 wint_t, w 表示“宽”。这是一个保证足够大以容纳任何wchar_t 以及EOF 的整数。 wchar_t(宽字符)保证足够宽以支持所有系统语言环境。
转换为 UTF-8 有点复杂,因为它的 codepage layout designed to be compatible with 7-bit ASCII。需要进行一些位移。
从 UTF-8 表开始。
U+0000 U+007F 0xxxxxxx
U+0080 U+07FF 110xxxxx 10xxxxxx
U+0800 U+FFFF 1110xxxx 10xxxxxx 10xxxxxx
U+10000 U+10FFFF 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx
把它变成一个大的 if/else if 语句。
wint_t codepoint_to_utf8( const wint_t codepoint ) {
wint_t utf8 = 0;
// U+0000 U+007F 0xxxxxxx
if( codepoint <= 0x007F ) {
}
// U+0080 U+07FF 110xxxxx 10xxxxxx
else if( codepoint <= 0x07FF ) {
}
// U+0800 U+FFFF 1110xxxx 10xxxxxx 10xxxxxx
else if( codepoint <= 0xFFFF ) {
}
// U+10000 U+10FFFF 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx
else if( codepoint <= 0x10FFFF ) {
}
else {
fprintf( stderr, "Codepoint %x is out of UTF-8 range\n", codepoint);
return -1;
}
return utf8;
}
然后开始填空。第一个很简单,只是代码点。
// U+0000 U+007F 0xxxxxxx
if( codepoint <= 0x007F ) {
utf8 = codepoint;
}
要做下一个,我们需要应用一个位掩码并进行一些位移。 C不支持二进制文字,所以我使用perl -wle 'printf("%x\n", 0b1100000010000000)'将二进制转换为十六进制
// U+0080 U+07FF 110xxxxx 10xxxxxx
else if( codepoint <= 0x00007FF ) {
// Start at 1100000010000000
utf8 = 0xC080;
// 6 low bits using the bitmask 00111111
// That fills in the 10xxxxxx part.
utf8 += codepoint & 0x3f;
// 5 high bits using the bitmask 11111000000
// Shift over 2 to jump the hard coded 10 in the low byte.
// That fills in the 110xxxxx part.
utf8 += (codepoint & 0x7c0) << 2;
}
剩下的交给你。
我们可以使用涉及每条逻辑的各种有趣的值来测试它。
int main() {
// https://codepoints.net/U+0041
printf("LATIN CAPITAL LETTER A: %x\n", codepoint_to_utf8(0x0041));
// https://codepoints.net/U+00A2
printf("Cent sign: %x\n", codepoint_to_utf8(0x00A2));
// https://codepoints.net/U+2603
printf("Snowman: %x\n", codepoint_to_utf8(0x02603));
// https://codepoints.net/U+10160
printf("GREEK ACROPHONIC TROEZENIAN TEN: %x\n", codepoint_to_utf8(0x10160));
printf("Out of range: %x\n", codepoint_to_utf8(0x00200000));
}
这是一个有趣的练习,但如果你想真正使用预先存在的库。 Gnome Lib has Unicode manipulation functions,还有很多缺失的 C 片段。