需要将字符串转换为 UTF8 字符串
Required to convert a String to UTF8 string
问题陈述:
我需要将生成的字符串转换为 UTF8 字符串,此生成的字符串具有扩展的 ascii 字符并且我在 Linux 系统上 (2.6.32-358.el6.x86_64)。
A POC is still in progress so I can only provide small code samples
and complete solution can be posted only once ready.
为什么我需要 UFT8(我扩展了 ascii 字符以存储在必须为 UTF8 的字符串中)。
我是如何进行的:
- 将生成的字符串转换为 wchar_t 字符串。
请看下面的示例代码
int main(){
char CharString[] = "Prova";
iconv_t cd;
wchar_t WcharString[255];
size_t size= mbstowcs(WcharString, CharString, strlen(CharString));
wprintf(L"%ls\n", WcharString);
wprintf(L"%s\n", WcharString);
printf("\n%zu\n",size);
}
这里有一个问题:
输出为
Prova?????
s
- 为什么这里没有打印尺寸?
- 为什么第二个 printf 只打印一个字符。
- 如果我在两个打印字符串之前都打印大小,那么只会打印 5 个字符串,并且两个字符串都从控制台中丢失。
继续第二部分:
现在我将有一个 wchar_t 字符串,我想将它转换为 UTF8 字符串
为此,我正在网上冲浪,发现 iconv 会在这里提供帮助。
在这里提问
这些是我在 manual
中找到的方法
**iconv_t iconv_open(const char *, const char *);
size_t iconv(iconv_t, char **, size_t *, char **, size_t *);
int iconv_close(iconv_t);**
在提供给 iconv 之前,我是否需要将 wchar_t 数组转换回 char 数组?
请就以上问题提出建议。
我正在谈论的扩展 ascii 请看下面标记的快照中的字母 i
对 utf8 使用 iconv 不是个好主意。自己实现utf8的定义就行了。这在描述 https://en.wikipedia.org/wiki/UTF-8 中用 C 很容易完成。
您甚至不需要 wchar_t,只需为您的角色使用 uint32_t。
如果您自己实现,您将学到很多东西,并且您的程序将因不使用 mb 或 iconv 函数而提高速度。
关于您的第一个问题(我将其解释为"why is all the output not what I expect"):
'??????'在哪里come from? 在调用 mbstowcs(WcharString, CharString, strlen(CharString))
中,最后一个参数 (strlen(CharString)
) 是 output buffer 的长度,而不是输入字符串。 mbstowcs
不会写入超过该数量的宽字符,包括 NUL 终止符。由于转换需要 6 个宽字符,包括终止符,而您只允许它写入 5 个宽字符,因此生成的宽字符串不会以 NUL 终止,当您尝试打印它时,您最终会在结束后打印垃圾转换后的字符串。因此 ?????
。您应该改用 wchar_t
中的输出缓冲区大小(在本例中为 255)。
为什么第二个wprintf
只打印一个字符?当你用宽字符串参数调用wprintf
时,你必须使用 %ls
格式代码(或者更准确地说,%s
转换需要使用 l
长度修饰符来限定)。如果你使用 %s
而没有 l
,那么 wprintf
会将字符串解释为 char*
,并且它会在输出时将每个字符转换为 wchar_t
它。但是,由于参数实际上是一个宽字符串,所以字符串中的第一个 wchar_t
是 L"p"
,这是某个整数大小的数字 0x70
。这意味着 wchar_t
的第二个字节(从末尾开始计数,因为你有一个小端架构)是一个 0,所以如果你把这个字符串当作 个字符的字符串 ,它会在p
之后立即终止。所以只打印一个字符。
为什么最后一个 printf
不打印任何东西? 在 C 中,输出流可以是 宽stream 或 byte stream,但您在打开流时未指定。 (而且,无论如何,标准输出已经为您打开。)这称为流的 方向 。新打开的流是无方向的,第一次输出到流时方向是固定的。如果第一个输出调用是宽调用,如wprintf
,则该流是宽流;否则,它是一个字节流。一旦设置,方向就固定了,你不能使用错误方向的输出调用。所以 printf
是非法的,它只会引发错误。
现在,让我们继续讨论您的第二个问题:我该怎么办?
首先你要清楚输入的是什么格式,你想怎么输出。在 Linux 上,您不太可能会想要使用 wchar_t
。输入字符串最可能的情况是它已经是 UTF-8,或者它是某种 ISO-8859-x 编码。输出的最可能情况是相同的:要么是 UTF-8,要么是某种 ISO-8859-x 编码。
不幸的是,您的程序无法知道控制台期望的编码。输出甚至可能不会转到控制台。同样,您的程序实际上无法知道输入字符串中使用的是哪种 ISO-8859-x 编码。 (如果它是字符串文字,则可能会在调用编译器时指定编码,但没有提供信息的标准方式。)
如果您因为非 ascii 字符无法正确显示而无法查看输出,您应该首先确保控制台配置为使用与程序输出相同的编码。如果程序将 UTF-8 发送到显示 ISO-8859-15 的控制台,则文本将无法正确显示。理论上,您的语言环境设置包括控制台使用的编码,但如果您使用的是远程控制台(例如,通过 Windows 机器上的 PuTTY),那么控制台不是 Linux 的一部分环境和默认语言环境可能不正确。最简单的修复方法是正确配置您的控制台,但也可以更改 Linux 语言环境。
您使用 mbstowcs
来自字节字符串的事实表明您认为原始字符串是 UTF-8 格式的。因此,问题似乎不太可能是您需要将其 转换为 UTF-8.
您当然可以使用 iconv
将字符串从一种编码转换为另一种编码;您无需通过 wchar_t
即可。但是您确实需要知道实际的输入编码和所需的输出编码。
问题陈述: 我需要将生成的字符串转换为 UTF8 字符串,此生成的字符串具有扩展的 ascii 字符并且我在 Linux 系统上 (2.6.32-358.el6.x86_64)。
A POC is still in progress so I can only provide small code samples and complete solution can be posted only once ready.
为什么我需要 UFT8(我扩展了 ascii 字符以存储在必须为 UTF8 的字符串中)。
我是如何进行的:
- 将生成的字符串转换为 wchar_t 字符串。
请看下面的示例代码
int main(){
char CharString[] = "Prova";
iconv_t cd;
wchar_t WcharString[255];
size_t size= mbstowcs(WcharString, CharString, strlen(CharString));
wprintf(L"%ls\n", WcharString);
wprintf(L"%s\n", WcharString);
printf("\n%zu\n",size);
}
这里有一个问题:
输出为
Prova?????
s
- 为什么这里没有打印尺寸?
- 为什么第二个 printf 只打印一个字符。
- 如果我在两个打印字符串之前都打印大小,那么只会打印 5 个字符串,并且两个字符串都从控制台中丢失。
继续第二部分:
现在我将有一个 wchar_t 字符串,我想将它转换为 UTF8 字符串
为此,我正在网上冲浪,发现 iconv 会在这里提供帮助。
在这里提问 这些是我在 manual
中找到的方法**iconv_t iconv_open(const char *, const char *);
size_t iconv(iconv_t, char **, size_t *, char **, size_t *);
int iconv_close(iconv_t);**
在提供给 iconv 之前,我是否需要将 wchar_t 数组转换回 char 数组?
请就以上问题提出建议。
我正在谈论的扩展 ascii 请看下面标记的快照中的字母 i
对 utf8 使用 iconv 不是个好主意。自己实现utf8的定义就行了。这在描述 https://en.wikipedia.org/wiki/UTF-8 中用 C 很容易完成。 您甚至不需要 wchar_t,只需为您的角色使用 uint32_t。 如果您自己实现,您将学到很多东西,并且您的程序将因不使用 mb 或 iconv 函数而提高速度。
关于您的第一个问题(我将其解释为"why is all the output not what I expect"):
'??????'在哪里come from? 在调用
mbstowcs(WcharString, CharString, strlen(CharString))
中,最后一个参数 (strlen(CharString)
) 是 output buffer 的长度,而不是输入字符串。mbstowcs
不会写入超过该数量的宽字符,包括 NUL 终止符。由于转换需要 6 个宽字符,包括终止符,而您只允许它写入 5 个宽字符,因此生成的宽字符串不会以 NUL 终止,当您尝试打印它时,您最终会在结束后打印垃圾转换后的字符串。因此?????
。您应该改用wchar_t
中的输出缓冲区大小(在本例中为 255)。为什么第二个
wprintf
只打印一个字符?当你用宽字符串参数调用wprintf
时,你必须使用%ls
格式代码(或者更准确地说,%s
转换需要使用l
长度修饰符来限定)。如果你使用%s
而没有l
,那么wprintf
会将字符串解释为char*
,并且它会在输出时将每个字符转换为wchar_t
它。但是,由于参数实际上是一个宽字符串,所以字符串中的第一个wchar_t
是L"p"
,这是某个整数大小的数字0x70
。这意味着wchar_t
的第二个字节(从末尾开始计数,因为你有一个小端架构)是一个 0,所以如果你把这个字符串当作 个字符的字符串 ,它会在p
之后立即终止。所以只打印一个字符。为什么最后一个
printf
不打印任何东西? 在 C 中,输出流可以是 宽stream 或 byte stream,但您在打开流时未指定。 (而且,无论如何,标准输出已经为您打开。)这称为流的 方向 。新打开的流是无方向的,第一次输出到流时方向是固定的。如果第一个输出调用是宽调用,如wprintf
,则该流是宽流;否则,它是一个字节流。一旦设置,方向就固定了,你不能使用错误方向的输出调用。所以printf
是非法的,它只会引发错误。
现在,让我们继续讨论您的第二个问题:我该怎么办?
首先你要清楚输入的是什么格式,你想怎么输出。在 Linux 上,您不太可能会想要使用 wchar_t
。输入字符串最可能的情况是它已经是 UTF-8,或者它是某种 ISO-8859-x 编码。输出的最可能情况是相同的:要么是 UTF-8,要么是某种 ISO-8859-x 编码。
不幸的是,您的程序无法知道控制台期望的编码。输出甚至可能不会转到控制台。同样,您的程序实际上无法知道输入字符串中使用的是哪种 ISO-8859-x 编码。 (如果它是字符串文字,则可能会在调用编译器时指定编码,但没有提供信息的标准方式。)
如果您因为非 ascii 字符无法正确显示而无法查看输出,您应该首先确保控制台配置为使用与程序输出相同的编码。如果程序将 UTF-8 发送到显示 ISO-8859-15 的控制台,则文本将无法正确显示。理论上,您的语言环境设置包括控制台使用的编码,但如果您使用的是远程控制台(例如,通过 Windows 机器上的 PuTTY),那么控制台不是 Linux 的一部分环境和默认语言环境可能不正确。最简单的修复方法是正确配置您的控制台,但也可以更改 Linux 语言环境。
您使用 mbstowcs
来自字节字符串的事实表明您认为原始字符串是 UTF-8 格式的。因此,问题似乎不太可能是您需要将其 转换为 UTF-8.
您当然可以使用 iconv
将字符串从一种编码转换为另一种编码;您无需通过 wchar_t
即可。但是您确实需要知道实际的输入编码和所需的输出编码。