我编写了以下代码:
#include <stdio.h>
#include <wchar.h>
int main() {
wchar_t wc[80];
wscanf(L"%ls", &wc);
wprintf(L"%ls", wc);
return 0;
}
我的终端支持 Unicode,在 Linux 上使用 gcc 8.2.1 编译。
最佳答案
这是您的程序的修复版本:
#include <stdlib.h>
#include <locale.h>
#include <stdio.h>
#include <wchar.h>
int main(void)
{
wchar_t ws[80];
if (!setlocale(LC_ALL, ""))
fprintf(stderr, "Warning: The C library does not support your current locale.\n");
if (fwide(stdin, 1) < 1)
fprintf(stderr, "Warning: The C library does not support wide standard input for your current locale.\n");
if (fwide(stdout, 1) < 1)
fprintf(stderr, "Warning: The C library does not support wide standard output for your current locale.\n");
if (wscanf(L"%79ls", ws) < 1) {
fprintf(stderr, "No input.\n");
exit(EXIT_FAILURE);
}
wprintf(L"%ls\n", ws);
return EXIT_SUCCESS;
}
setlocale()
调用告诉 C 库使用当前配置的区域设置。如果您不这样做,C 库将使用其默认区域设置(C/POSIX 区域设置),该区域设置通常使用 ASCII 字符集(而不是 UTF-8)。
fwide(stdin, 1)
和 fwide(stdout, 1) 调用告诉 C 库您将使用具有标准输入的宽输入函数和具有标准输出的宽输出函数。如果 C 库不支持当前语言环境,它们将返回 -1;我相信目前 Windows 中基于 UTF-8 的区域设置会发生这种情况,因为 Microsoft 希望程序员为此使用他们的专有扩展。
不需要 fwide()
调用,因为 C 库会根据您为每个流使用的第一个函数进行猜测。我确实建议明确使用它们,以便用户知道他们当前的配置或 C 库支持是否存在可疑/错误/不受支持的情况。毕竟,这只是添加了几行。
扫描字符串时,应始终在模式中包含允许的最大长度(紧跟在 %
之后)。由于 C 字符串具有终止 nul 字符('\0'
对于窄字符串,L'\0'
对于宽字符串),因此缓冲区必须至少长一个。由于 ws
是一个 80 个宽字符的数组,因此 wscanf()
可以将最多 79 个字符的字符串扫描到其中。
所有扫描函数(scanf()
、wscanf()
、fscanf()
、fwscanf()
> 等)返回成功转换的次数,或 EOF/WEOF。例如,如果用户运行 true | ./thisprogram
,标准输入中没有输入,wscanf()
调用将返回 WEOF。除了一些罕见的异常(使用抑制转换来消耗/跳过数据,或使用 %n
进行转换)之外,您将需要检查返回值。如果您不检查上面示例中的返回值 (true | ./thisprogram
),您最终会打印未初始化的宽字符缓冲区。这不好;它要么不打印任何内容,打印垃圾,要么使程序崩溃:这是未定义的行为。
(同样重要的是要记住,如果转换失败,失败的部分将保留在输入中;它不会被消耗或丢弃。它只是坐在那里,除非您消耗它。)
某些 shell 在最终输出行的末尾添加一个 %
字符(如果该行不以换行符结尾)。其他 shell 紧随其后放置自己的提示符。这不是一个错误,只是看起来很奇怪。因此,最好始终在输出末尾添加换行符。
默认情况下,标准输出也是行缓冲的。例如,在上面的程序中,使用wprintf(L"foo")
是否不意味着输出宽字符串foo
;它通常只是由标准 C 库缓冲,并在一段时间后输出。您可以使用 fflush(stdout);
告诉标准库输出特定流缓冲区中的所有内容,例如标准输出。这适用于正常/窄流和宽流。然而,当程序退出时,C 库将自动刷新缓冲区。
关于无法读取并回显 C 中的 unicode 输入,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/54346259/