C语言与中文的一些测试 (Win, UTF8源码)

C语言与中文的一些测试 (Win, UTF8源码)

C语言如何输出中文?

测试前提:1. 源代码为UTF8编码的文件。2. 中文Windows系统。

测试环境:Windows 10,MinGW-w64 8.1。

如果用Windows SDK,效果可能和我的测试有极大不同,可能可以用UTF8,具体见评论区。

你好 的编码信息

  • GBK: C4E3 BAC3
  • UTF8: E4BDA0 E5A5BD
  • UTF16 LE:4F60 597D

char硬编码字符串

#include <stdio.h>
#include <stdlib.h>
void ShowBytes(char *str)
    if (*str == '\0')
        printf("EMPTY");
    while (*str != '\0')
        printf("%hhX ", (unsigned char)*str++);
    // 中文的char的最高位为1,如果直接赋值给int或unsigned,会先进行位扩展,再改变类型,结果就是负的
int main()
    system("chcp 936");
    // system("chcp 65001");
    char str[] = "你好";
    puts(str);
    ShowBytes(str);
}

char硬编码结果

chcp 936:

浣犲ソ
E4 BD A0 E5 A5 BD

chcp 65001:

你好
E4 BD A0 E5 A5 BD

char硬编码结论

  1. 硬编码的char字符串与文件编码一致,此处是UTF8。
  2. 输出 字符串时用的编码与chcp一致。

wchar_t硬编码字符串

#include <locale.h>
#include <stdio.h>
#include <stdlib.h>
#include <wchar.h>
void ShowBytesWide(wchar_t *str)
    if (*str == '\0')
        printf("EMPTY");
    while (*str != '\0')
        wprintf(L"%hX ", (wchar_t)*str++);
int main()
    system("chcp 936");
    // system("chcp 65001");
    setlocale(LC_ALL, "chs");
    wchar_t str[] = L"你好";
    wprintf(L"%ls\n", str);
    // fputws(str, stdout);
    ShowBytesWide(str);
}

wchar_t硬编码结果

chcp 936与chcp 65001:

你好
4F60 597D

如果注释掉setlocale,函数不会输出。

如果注释掉wprintf而改用fputws,函数不会输出,原因不明。

wchar_t硬编码结论

  1. 硬编码的wcahr_t字符串储存的是UTF16的编码。
  2. 必须使用setlocale,否则无法输出。
  3. wprintf输出时,会把wchar_t*转换成当前locale的编码,然后输出。

char手动输入输出

int main()
    // system("chcp 936");
    system("chcp 65001");
    char str[10];
    gets(str);
    // scanf("%s", str);
    puts(str);
    ShowBytes(str);
}

char手动结果

chcp 936:

你好
C4 E3 BA C3

chcp 65001:

你好
EMPTY

char手动结论

  1. chcp 936时能成功读入,且编码为GBK。
  2. chcp 65001时gets和scanf无法成功读入!

wchar_t手动输入输出

int main()
    system("chcp 936");
    // system("chcp 65001");
    setlocale(LC_ALL, "chs");
    wchar_t str[10];
    wscanf(L"%ls", str);
    wprintf(L"%ls\n", str);
    ShowBytesWide(str);
}

wchar_t手动结果

chcp 936:

你好
4F60 597D

chcp 65001:

你好
EMPTY

wchar_t手动结论

  1. chcp 936时能成功读入,且编码为UTF16。
  2. chcp 65001时wscanf无法成功读入!

最终结论

  1. 说好的UTF8呢?怎么全都是GBK和UTF16?
    理论上char+chcp 65001就是UTF8,然而读取失败。没有办法手动输入UTF8编码的字符串,只有编译时已经存在的能用。
  2. 应该如何处理中文?
    chcp 936 + wchar_t。因为这是唯一成功的方案。

附加测试:wprintf的%ls

根据 cppreference ,%s对应的是 char* ,%ls对应的才是 wchar_t*

#include <locale.h>
#include <stdio.h>
#include <wchar.h>
int main()
#ifdef linux
    setlocale(LC_ALL, "zh_CN.UTF-8");
#else
    setlocale(LC_ALL, "chs");
#endif
    char *strc = "c你好";
    wchar_t *strw = L"w你好";
    wprintf(L"%s\n", strc);
    wprintf(L"%s\n", strw);
    wprintf(L"%ls\n", strc);
    wprintf(L"%ls\n", strw);
}

Windows下的测试结果:

w你好