printf()是否关心语言环境?

时间:2018-06-04 14:39:29

标签: c linux gcc

wprintf()wchar_t字符串作为参数,并以指定的区域设置字符编码打印字符串。

但是我注意到当使用printf()并将其传递给UTF-8字符串时,无论指定的语言环境字符编码如何,都将始终打印UTF-8字符串(例如,如果UTF-8字符串包含阿拉伯字符,并且区域设置设置为" C"(不是" C.UTF-8"),然后仍将打印阿拉伯字符。

我是否认为printf()并不关心语言环境?

3 个答案:

答案 0 :(得分:3)

True printf不关心c-strings的语言环境。如果你传递一个UTF-8字符串,它对它一无所知,只看到一个字节序列(希望由ascii NUL终止)。然后,字节按原样传递给输出,并由终端解释(或任何输出)。如果终端能够解释UTF-8序列,那么它会这样做(如果没有,它会尝试按照配置的方式解释它,Latin-1或类似),如果它也能够正确打印它们那么它就这样做了(有时它没有正确的字体/字形,并将未知字符打印为?或类似)。

答案 1 :(得分:2)

这是UTF-8的一大优点(也许是最大的优点):它只是一串相当普通的字节。 如果 您的代码编辑环境知道如何让您输入

printf("Cööl!\n");

如果 您的显示环境(例如您的终端窗口)知道如何显示它,您只需编写并运行它,它就可以工作(因为它)听起来像你发现的那样。)

因此,您不需要特殊的运行时支持,您不需要特殊的头文件或库或任何东西,您不需要以某种奇特的新Unicodey方式编写代码 - 您可以继续使用普通的C字符串和printf以及像你习惯的朋友一样,这一切都正常。

当然,那两个 if 可能是大的。如果您无法弄清楚如何(或您的代码编辑环境不会让您)键入字符,或者如果您的显示环境没有显示它们,您可能会被卡住,或者您可能需要做一些艰苦的工作毕竟。 (显示环境没有正确显示C程序的UTF-8输出显然非常常见,基于SO在此处提出问题的次数。)

另请参阅“UTF-8 Everywhere”宣言。

(现在,所有这些都说明了,这并不意味着printf根本不关心语言环境设置。printf可能关心的语言环境的各个方面,以及可能存在printf可能需要特定处理的字符集和编码,依赖于语言环境。但由于printf不需要做任何特殊的事情来使UTF-8正常工作,语言环境的一个方面 - 虽然它是一个大问题 - 但最终不会影响printf。)

答案 2 :(得分:1)

让我们考虑以下简单程序,如果在没有命令行参数的情况下运行,则使用printf()打印宽字符串,否则使用wprintf()

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

const wchar_t hello1[] = L"تحية طيبة";
const wchar_t hello2[] = L"Tervehdys";

int main(int argc, char *argv[])
{
    if (!setlocale(LC_ALL, ""))
        fprintf(stderr, "Warning: Current locale is not supported by the C library.\n");

    if (argc <= 1) {
        printf("printf 1: %ls\n", hello1);
        printf("printf 2: %ls\n", hello2);
    } else {
        wprintf(L"wprintf: %ls\n", hello1);
        wprintf(L"wprintf: %ls\n", hello2);
    }

    return EXIT_SUCCESS;
}

使用GNU C库和任何UTF-8语言环境:

$ ./example
printf 1: تحية طيبة
printf 2: Tervehdys

$ ./example wide
wprintf: تحية طيبة
wprintf: Tervehdys

即。两者都产生完全相同的输出。但是,如果我们在C / POSIX语言环境中运行该示例(仅支持ASCII),我们得到

$ LANG=C LC_ALL=C ./example
printf 1: printf 2: Tervehdys

,即第一个printf()停在第一个非ASCII字符处(这就是第二个printf()打印在同一行上的原因);

$ LANG=C LC_ALL=C ./example wide
wprintf: ???? ????
wprintf: Tervehdys

即。 wprintf()使用?替换当前区域设置使用的字符集中无法表示的宽字符。

因此,如果我们考虑GNU C库(表现出这种行为),那么我们必须说是的,printf关心语言环境,尽管它实际上主要关心的是使用的字符集。语言环境,而不是语言环境本身:

  尝试打印当前字符集(由语言环境定义)无法表示的宽字符串时,

printf()将停止。
wprintf()会为这些字符输出问号。

x86-64(amd64)上的libc6-2.23-0ubuntu10对printf格式字符串中的多字节字符进行了一些替换,但是用%s打印的字符串中的多字节字符按原样打印。这意味着确切地说出打印的内容以及当printf()放弃第一个多字节或宽字符时它无法转换或者只是按原样打印时有点复杂。

然而,wprintf()非常稳固。 (如果您尝试使用当前语言环境使用的字符集中无法表示的多字节字符来打印窄字符串,也可能会阻塞,但对于宽字符串,它似乎工作得非常好。)

请注意,POSIX.1 C库还提供iconv_open()iconv()iconv_close()来转换字符串,以及mbstowcs()wcstombs()在宽和窄/多字节字符串之间转换。您还可以使用asprintf()从窄字符串和/或宽字符串(分别为%s%ls)创建动态分配的窄字符串。