9

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

读取和写入宽字符函数(如getwchar()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()我只需要字符串长度和控制台宽度(

4

3 回答 3

9

只要语言环境设置正确,使用宽字符函数在使用 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. 但是,如果您只在stdinand上使用宽字符函数stdout(包括您链接到的任何代码),您将不会遇到任何问题。

于 2013-03-16T23:21:12.413 回答
9

控制宽字符 stdio 函数的行为及其与语言环境的关系的相关文本来自 POSIX XSH 2.5.2 Stream Orientation and Encoding Rules:

http://pubs.opengroup.org/onlinepubs/9699919799/functions/V2_chap02.html#tag_15_05_02

基本上,宽字符 stdio 函数总是写入在流变得面向宽时有效的编码(根据LC_CTYPE语言环境类别) ;FILE这意味着第一次在其上调用宽 stdio 函数,或fwide用于将方向设置为宽。因此,只要LC_CTYPE在您开始使用流时,适当的语言环境实际上与所需的“系统”编码(例如 UTF-8)相匹配,一切都应该没问题。

但是,您不应忽略的一个重要考虑因素是,您不能在同FILE一流上混合使用面向字节和宽的操作。不遵守此规则不是可报告的错误;它只会导致未定义的行为。由于大量库代码假设stderr是面向字节的(有些stdout甚至. 如果这样做,则需要非常小心使用哪些库函数。

真的,我想不出任何理由使用面向广泛的功能。fprintf完全能够使用说明符将宽字符串发送到面向字节的FILE%ls

于 2013-03-17T03:34:34.770 回答
-1

请勿fputs与 ASCII 以外的任何内容一起使用。

如果你想写下来让我们说 UTF8,然后使用一个函数返回 utf8 字符串使用的实际大小并使用 fwrite 写入好的字节数,而不用担心\0字符串内部的恶性 ' '。

于 2013-03-16T23:05:21.853 回答