C 中的宽字符输入/输出是否始终读取/写入正确的(系统默认)编码?

2023-11-23

我主要对类 Unix 系统(例如,便携式 POSIX)感兴趣,因为 Windows 似乎对宽字符做了奇怪的事情。

执行读取和写入宽字符功能(例如getwchar() and putwchar())总是“做正确的事”,例如从 utf-8 读取并在设置的语言环境时写入 utf-8,或者我是否必须手动调用wcrtomb()并使用例如打印字符串fputs()?在我的系统(openSUSE 12.3)上$LANG被设定为en_GB.UTF-8他们似乎确实做了正确的事情(检查输出,我看到看起来像 UTF-8 的内容,即使字符串是使用 wchar_t 存储并使用宽字符函数编写的)。

但是我不确定这是否得到保证。例如cprogramming.com指出:

[宽字符] 不应用于输出,因为虚假零 字节和其他具有常见含义的低位 ASCII 字符(例如“/” 和 '\n')可能会散布在整个数据中。

这似乎表明输出宽字符(大概使用宽字符输出函数)可能会造成严重破坏。

由于 C 标准似乎根本没有提到编码,我真的不知道在使用 wchar_t 时谁/何时/如何应用编码。所以我的问题基本上是,当我的应用程序不需要知道所使用的编码时,专门读取、写入和使用宽字符是否是正确的做法。我只需要字符串长度和控制台宽度(wcswidth()),所以对我来说,在处理文本时到处使用 wchar_t 似乎是理想的选择。


只要区域设置正确设置,在使用 UTF-8 的系统上使用宽字符函数处理 UTF-8 文件就不会有任何问题。他们将能够正确解释事物,即他们将根据需要将字符视为 1-4 个字节(在输入和输出中)。你可以通过这样的方式来测试它:

#include <stdio.h>
#include <locale.h>
#include <wchar.h>

int main()
{
    setlocale(LC_CTYPE, "en_GB.UTF-8");
    // setlocale(LC_CTYPE, ""); // to use environment variable instead
    wchar_t *txt = L"£Δᗩ";

    wprintf(L"The string %ls has %d characters\n", txt, wcslen(txt));
}

$ gcc -o loc loc.c && ./loc
The string £Δᗩ has 3 characters

如果您不小心在多字节字符串上使用标准函数(特别是字符函数),事情就会开始崩溃,例如等价的:

char *txt = "£Δᗩ";
printf("The string %s has %zu characters\n", txt, strlen(txt));

$ gcc -o nloc nloc.c && ./nloc
The string £Δᗩ has 7 characters

该字符串在这里仍然可以正确打印,因为它本质上只是一个字节流,并且由于系统期望 UTF-8 序列,因此它们被完美地翻译。当然strlen正在报告字符串中的字节数,7(加上\0),不知道字符和字节是不等价的。

在这方面,由于 ASCII 和 UTF-8 之间的兼容性,只要您小心,您通常可以将 UTF-8 文件视为简单的多字节 C 字符串。

还有一定程度的灵活性。可以轻松地将标准 C 字符串(作为多字节字符串)转换为宽字符串:

char *stdtxt = "ASCII and UTF-8 €£¢";
wchar_t buf[100]; 
mbstowcs(buf, stdtxt, 20);

wprintf(L"%ls has %zu wide characters\n", buf, wcslen(buf));

Output:
ASCII and UTF-8 €£¢ has 19 wide characters

一旦您在流上使用了宽字符函数,它就会设置为宽方向。如果稍后想要使用标准字节 I/O 函数,则需要先重新打开流。这可能就是为什么建议不要使用它的原因stdout。但是,如果您仅使用宽字符函数stdin and stdout(包括您链接到的任何代码),您不会有任何问题。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

C 中的宽字符输入/输出是否始终读取/写入正确的(系统默认)编码? 的相关文章

随机推荐