有没有办法从 UTF8 转换为 ISO-8859-1?
Posted
技术标签:
【中文标题】有没有办法从 UTF8 转换为 ISO-8859-1?【英文标题】:Is there a way to convert from UTF8 to ISO-8859-1? 【发布时间】:2012-06-24 18:27:00 【问题描述】:我的软件在 UTF8 中获取了一些字符串,而不是我需要转换为 ISO 8859 1。我知道 UTF8 域大于 ISO 8859。但是 UTF8 中的数据之前已经从 ISO 上转换,所以我不应该错过任何东西.
我想知道是否有一种简单/直接的方法可以将 UTF8 转换为 iso-8859-1。
【问题讨论】:
如果您使用的是进行转换的库,它还应该有一些东西可以将其转换回来。假设您没有更改字符串中的任何字符,那么只需将其返回就可以了。 【参考方案1】:这是一个您可能会觉得有用的函数:utf8_to_latin9()
。它转换为ISO-8859-15
(包括欧元,ISO-8859-1
没有),但也适用于ISO-8859-1
的ISO-8859-1
转换部分ISO-8859-1
->UTF-8
->ISO-8859-1
往返。
该函数忽略类似于//IGNORE
iconv 标志的无效代码点,但不重构已分解的 UTF-8 序列;也就是说,它不会将U+006E U+0303
变成U+00F1
。我不打扰重新组合,因为 iconv 也没有。
该函数对字符串访问非常小心。它永远不会扫描超出缓冲区。输出缓冲区必须比长度长一个字节,因为它总是附加字符串结尾的 NUL 字节。该函数返回输出中的字符(字节)数,不包括字符串结尾的 NUL 字节。
/* 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);
请注意,您可以在 to_latin9()
函数中为特定代码点添加自定义音译,但您只能替换一个字符。
正如目前编写的那样,该函数可以安全地进行就地转换:输入和输出指针可以相同。输出字符串永远不会比输入字符串长。如果您的输入字符串有空间容纳额外的字节(例如,它有 NUL 终止字符串),您可以安全地使用上述函数将其从 UTF-8 转换为 ISO-8859-1/15。我故意这样写,因为它应该在嵌入式环境中为您节省一些精力,尽管这种方法有点有限。自定义和扩展。
编辑:
我包含一对转换函数 in an edit to this answer 用于拉丁语 1/9 到/从 UTF-8 转换(ISO-8859-1 或 -15 到/从 UTF-8);主要区别在于这些函数返回一个动态分配的副本,并保持原始字符串不变。
【讨论】:
【参考方案2】:iconv - 执行字符集转换
size_t iconv(iconv_t cd, char **inbuf, size_t *inbytesleft, char **outbuf, size_t *outbytesleft);
iconv_t iconv_open(const char *tocode, const char *fromcode);
tocode
是 "ISO_8859-1"
,fromcode
是 "UTF-8"
。
工作示例:
#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;
【讨论】:
谢谢,我遇到的主要问题是我忘了说明我的软件在嵌入式 Linux 上运行并且 iconv 不可用。 你可以为你的 linux 编译 iconv。你的 linux 使用 glibc 吗?如果是,它的兼容实现称为gconv
:gnu.org/software/libc/manual/html_node/…
感谢分享。您应该调用iconv_close()
来释放iconv_open()
分配的资源。只需将iconv_close(cd);
放在printf("%s -> %s\n", input, output);
行之前。
@silvioprog,感谢您的评论!我添加了这条线。【参考方案3】:
以下示例也使用了 iconv 库。 即使您有一个包含混合 UTF-8 和 ISO-8859-1 字符的文件(或输入流),它也可以工作(例如,如果您有一个 UTF-8 文件并在使用 ISO 的环境中编辑它,则可能发生这种情况-8859-1)。
int Utf8ToLatin1(char* input, char* output, size_t size)
size_t in_left = size;
size_t out_left = size;
char *in_buf = input;
char *out_buf = output;
iconv_t cd = iconv_open("ISO_8859-1", "UTF-8");
if (cd == (iconv_t)-1)
(void) fprintf(stderr, "iconv_open() failed, msg encoding will be kept!");
strncpy(output, input, size);
return -1;
do
if (iconv(cd, &in_buf, &in_left, &out_buf, &out_left) == (size_t) -1)
if (errno == EILSEQ)
/* Input conversion stopped due to an input byte that
* does not belong to the input codeset.
*/
printf("Input conversion stopped due to an input byte that does not belong to the input codeset.\n");
*out_buf= *in_buf;
out_buf++ ;out_left--;
in_buf++ ;in_left--;
else if (errno == E2BIG)
/* Input conversion stopped due to lack of space in
* the output buffer.
*/
printf("Input conversion stopped due to lack of space in the output buffer.\n");
perror("iconv failed!, propably the encoding is already Latin, msg encoding will be kept!\n");
strncpy(output, input, size);
return -1;
else if (errno == EINVAL)
/* Input conversion stopped due to an incomplete
* character or shift sequence at the end of the
* input buffer.
*/
printf("Input conversion stopped due to an incomplete character or shift sequence at the end of the input buffer.\n");
*out_buf= *in_buf;
out_buf++ ;out_left--;
in_buf++ ;in_left--;
while (in_left > 0 && out_left > 0);
*out_buf = 0;
iconv_close(cd);
printf("*********************************************************\n");
printf("ISO-8859-1:\n %s\n", input, output);
return 0;
【讨论】:
请不要使用strncpy(output, input, size);
如果输出有size
或更多字节,则不会以空结尾。此外,最终的*out_buf = 0;
可能会超出目标数组的末尾。
另外 OP 在评论中写道 iconv 在目标系统上不可用,但约束可能在 8 年内发生了变化。以上是关于有没有办法从 UTF8 转换为 ISO-8859-1?的主要内容,如果未能解决你的问题,请参考以下文章
Oracle:从 WE8ISO8859P1 转换为 AL32UTF8
如何将汉字转换为iso-8859-1的字符串 - PHP进阶讨论
python怎么解码ISO-8859-2格式.并转换为utf-8