代码点 1 到 127 在 Latin-9 (ISO-8859-15) 和 UTF-8 中是相同的。
Latin-9 中的代码点 164 在 UTF-8 中是 U+20AC,\xe2\x82\xac = 226 130 172。
Latin-9 中的代码点 166 在 UTF-8 中是 U+0160,\xc5\xa0 = 197 160。
Latin-9 中的代码点 168 在 UTF-8 中是 U+0161,\xc5\xa1 = 197 161。
Latin-9 中的代码点 180 在 UTF-8 中是 U+017D,\xc5\xbd = 197 189。
Latin-9 中的代码点 184 在 UTF-8 中是 U+017E,\xc5\xbe = 197 190。
Latin-9 中的代码点 188 在 UTF-8 中是 U+0152,\xc5\x92 = 197 146。
Latin-9 中的代码点 189 在 UTF-8 中是 U+0153,\xc5\x93 = 197 147。
Latin-9 中的代码点 190 在 UTF-8 中是 U+0178,\xc5\xb8 = 197 184。
Latin-9 中的代码点 128 .. 191(上面列出的除外)都映射到 UTF-8 中的 \xc2\x80 .. \xc2\xbf = 194 128 .. 194 191。
Latin-9 中的代码点 192 .. 255 都映射到 UTF-8 中的 \xc3\x80 .. \xc3\xbf = 195 128 .. 195 191。
这意味着 Latin-9 代码点 1..127 在 UTF-8 中是一个字节长,代码点 164 是三个字节长,其余(128..163 和 165..255)是两个字节长.
如果您首先扫描 Latin-9 输入字符串,您可以确定生成的 UTF-8 字符串的长度。如果您想要或需要 - 毕竟您正在使用嵌入式系统 - 您可以就地进行转换,从头到尾向后工作。
编辑:
这里有两个函数可以用于转换。这些会在使用后返回您需要的动态分配副本free()。它们仅在发生错误时返回NULL(内存不足,errno == ENOMEM)。如果给定 NULL 或要转换的空字符串,函数将返回一个动态分配的空字符串。
换句话说,当你完成这些函数时,你应该总是在这些函数返回的指针上调用free()。 (free(NULL) 被允许并且什么都不做。)
如果输入不包含零字节,latin9_to_utf8() 已被验证产生与iconv 完全相同的输出。该函数使用标准 C 字符串,即零字节表示字符串结束。
如果输入仅包含 ISO-8859-15 中的 Unicode 代码点且不包含零字节,则 utf8_to_latin9() 已被验证产生与 iconv 完全相同的输出。当给定随机 UTF-8 字符串时,该函数将 Latin-1 中的八个代码点映射到 Latin-9 等价物,即货币符号到欧元; iconv 要么忽略它们,要么考虑这些错误。
utf8_to_latin9() 行为意味着函数适用于 both Latin 1->UTF-8->Latin 1 和 Latin 9->@ 987654358@->Latin9往返。
#include <stdlib.h> /* for realloc() and free() */
#include <string.h> /* for memset() */
#include <errno.h> /* for errno */
/* Create a dynamically allocated copy of string,
* changing the encoding from ISO-8859-15 to UTF-8.
*/
char *latin9_to_utf8(const char *const string)
{
char *result;
size_t n = 0;
if (string) {
const unsigned char *s = (const unsigned char *)string;
while (*s)
if (*s < 128) {
s++;
n += 1;
} else
if (*s == 164) {
s++;
n += 3;
} else {
s++;
n += 2;
}
}
/* Allocate n+1 (to n+7) bytes for the converted string. */
result = malloc((n | 7) + 1);
if (!result) {
errno = ENOMEM;
return NULL;
}
/* Clear the tail of the string, setting the trailing NUL. */
memset(result + (n | 7) - 7, 0, 8);
if (n) {
const unsigned char *s = (const unsigned char *)string;
unsigned char *d = (unsigned char *)result;
while (*s)
if (*s < 128) {
*(d++) = *(s++);
} else
if (*s < 192) switch (*s) {
case 164: *(d++) = 226; *(d++) = 130; *(d++) = 172; s++; break;
case 166: *(d++) = 197; *(d++) = 160; s++; break;
case 168: *(d++) = 197; *(d++) = 161; s++; break;
case 180: *(d++) = 197; *(d++) = 189; s++; break;
case 184: *(d++) = 197; *(d++) = 190; s++; break;
case 188: *(d++) = 197; *(d++) = 146; s++; break;
case 189: *(d++) = 197; *(d++) = 147; s++; break;
case 190: *(d++) = 197; *(d++) = 184; s++; break;
default: *(d++) = 194; *(d++) = *(s++); break;
} else {
*(d++) = 195;
*(d++) = *(s++) - 64;
}
}
/* Done. Remember to free() the resulting string when no longer needed. */
return result;
}
/* Create a dynamically allocated copy of string,
* changing the encoding from UTF-8 to ISO-8859-15.
* Unsupported code points are ignored.
*/
char *utf8_to_latin9(const char *const string)
{
size_t size = 0;
size_t used = 0;
unsigned char *result = NULL;
if (string) {
const unsigned char *s = (const unsigned char *)string;
while (*s) {
if (used >= size) {
void *const old = result;
size = (used | 255) + 257;
result = realloc(result, size);
if (!result) {
if (old)
free(old);
errno = ENOMEM;
return NULL;
}
}
if (*s < 128) {
result[used++] = *(s++);
continue;
} else
if (s[0] == 226 && s[1] == 130 && s[2] == 172) {
result[used++] = 164;
s += 3;
continue;
} else
if (s[0] == 194 && s[1] >= 128 && s[1] <= 191) {
result[used++] = s[1];
s += 2;
continue;
} else
if (s[0] == 195 && s[1] >= 128 && s[1] <= 191) {
result[used++] = s[1] + 64;
s += 2;
continue;
} else
if (s[0] == 197 && s[1] == 160) {
result[used++] = 166;
s += 2;
continue;
} else
if (s[0] == 197 && s[1] == 161) {
result[used++] = 168;
s += 2;
continue;
} else
if (s[0] == 197 && s[1] == 189) {
result[used++] = 180;
s += 2;
continue;
} else
if (s[0] == 197 && s[1] == 190) {
result[used++] = 184;
s += 2;
continue;
} else
if (s[0] == 197 && s[1] == 146) {
result[used++] = 188;
s += 2;
continue;
} else
if (s[0] == 197 && s[1] == 147) {
result[used++] = 189;
s += 2;
continue;
} else
if (s[0] == 197 && s[1] == 184) {
result[used++] = 190;
s += 2;
continue;
}
if (s[0] >= 192 && s[0] < 224 &&
s[1] >= 128 && s[1] < 192) {
s += 2;
continue;
} else
if (s[0] >= 224 && s[0] < 240 &&
s[1] >= 128 && s[1] < 192 &&
s[2] >= 128 && s[2] < 192) {
s += 3;
continue;
} else
if (s[0] >= 240 && s[0] < 248 &&
s[1] >= 128 && s[1] < 192 &&
s[2] >= 128 && s[2] < 192 &&
s[3] >= 128 && s[3] < 192) {
s += 4;
continue;
} else
if (s[0] >= 248 && s[0] < 252 &&
s[1] >= 128 && s[1] < 192 &&
s[2] >= 128 && s[2] < 192 &&
s[3] >= 128 && s[3] < 192 &&
s[4] >= 128 && s[4] < 192) {
s += 5;
continue;
} else
if (s[0] >= 252 && s[0] < 254 &&
s[1] >= 128 && s[1] < 192 &&
s[2] >= 128 && s[2] < 192 &&
s[3] >= 128 && s[3] < 192 &&
s[4] >= 128 && s[4] < 192 &&
s[5] >= 128 && s[5] < 192) {
s += 6;
continue;
}
s++;
}
}
{
void *const old = result;
size = (used | 7) + 1;
result = realloc(result, size);
if (!result) {
if (old)
free(old);
errno = ENOMEM;
return NULL;
}
memset(result + used, 0, size - used);
}
return (char *)result;
}
虽然iconv() 通常是字符集转换的正确解决方案,但上述两个函数在嵌入式或其他受限环境中肯定有用。