C语言内处理不同的字符编码一直是个重要问题,正好今天遇到了需要处理读取/输出中文的问题,把研究结果在这里记录一下。
wchar_t
类型
说明
定义于头文件 wchar.h
内,存储宽字符类型;wchar_t
类型的宽字符串可与使用 char
类型的多字节字符串互相转换。
宽字符串字面量在字面量前加 L
标记。e.g. L"s-char-sequence"
相关类型
wint_t
类型是可保有任何合法宽字符,并至少多出一个值的整数类型 。
I/O 函数
本节参考自中文 cppreference 的文件输入/输出一节。
无格式 I/O
函数名 | 函数原型 | 说明 |
---|---|---|
fgetwc /getwc | wint_t fgetwc( FILE *stream ); | 从文件流获取一个宽字符 |
fgetws | wchar_t *fgetws( wchar_t *str, int count, FILE *stream ); | 从文件流获取一个宽字符串 |
fputwc/putwc | wint_t fputwc( wchar_t ch, FILE *stream ); | 将一个宽字符写入文件流 |
fputws | int fputws( const wchar_t *str, FILE *stream ); | 将一个宽字符串写入文件流 |
getwchar | wint_t getwchar(void); | 从 stdin 读取一个宽字符 |
putwchar | wint_t putwchar( wchar_t ch ); | 将一个宽字符写入stdout |
ungetwc | wint_t ungetwc( wint_t ch, FILE *stream ); | 将一个宽字符送回文件流 |
有格式 I/O
wscanf
系列
函数名 | 函数原型 | 说明 |
---|---|---|
wscanf | int wscanf( const wchar_t *format, ... ); | 从 stdin 读取格式化宽字符输入 |
fwscanf | int fwscanf( FILE *stream, const wchar_t *format, ... ); | 从文件流读取格式化宽字符输入 |
swscanf | int swscanf( const wchar_t *buffer, const wchar_t *format, ... ); | 从缓冲区读取格式化宽字符输入 |
wprintf
系列
函数名 | 函数原型 | 说明 |
---|---|---|
wprintf | int wprintf( const wchar_t *format, ... ); | 打印格式化输出到 stdout |
fwprintf | int fwprintf( FILE *stream, const wchar_t* format, ... ); | 打印格式化输出到文件流 |
swprintf | int swprintf( wchar_t *buffer, size_t bufsz, const wchar_t* format, ... ); | 打印格式化输出到缓冲区 |
格式化说明符与使用方式
格式化说明符 | 说明 |
---|---|
%lc | 对应 wint_t 类型 |
%ls | 对应 wchar_t * 类型 |
wscanf
/wprintf
系列中的 format
参数若是宽字符串字面量,亦须确保加上了对应的 L
标记。
swprintf
中的 bufsz
参数保证最多会写入 bufsz - 1
个宽字符,再加空终止符。
注意:若 wscanf
系列函数在赋值首个接收参数前出现读取失败,返回值仍为 EOF
而非 wchar.h
中定义的宏 WEOF
。
示例
|
|
窄字符 scanf
/printf
系列函数对宽字符(串)的处理
本节内容参考自浅谈C中的wprintf和宽字符显示。
窄字符之 scanf
/printf
系列函数亦可使用 %ls
/%lc
格式说明符来处理 wchar_t *
/wchar_t
类型。但由于 scanf
/printf
系列函数用于 byte stream,其输入/输出流中的每个字符占 1 byte
;wscanf
/wprintf
系列函数用于 wide stream,其输入/输出流中的每个字符多于 1 byte
,故其有区别如下:
组合 | 说明 |
---|---|
scanf /printf + %s | scanf /printf 将将指针对应缓冲区中的内容视作普通字符串,之后逐个字节输出 |
scanf /printf + %ls | scanf /printf 将指针对应缓冲区中的内容视作宽字符串,按照 locale 的设定,将其中的每个字符隐式调用 wcrtomb() 函数将其转换成多字节字符串,之后逐个字节输出 |
wscanf /wprintf + %s | wscanf /wprintf 将指针对应缓冲区中的内容视作普通字符串,按照 locale 的设定,将其中的每个字符隐式调用 mbrtowc() 函数将其转换成宽字符串,之后逐个宽字符输出 |
wscanf /wprintf + %ls | wscanf /wprintf 将指针对应缓冲区中的内容视作宽字符串,之后逐个宽字符输出 |
文件读写
本节内容参考自 Linux Manual Page 和 Visual Studio 2019 文档。
C语言没有处理宽字符串或其他编码的特殊文件I/O函数,若需指定采用特殊编码读写文件,需采用一个gcc/msvc扩展,在读写模式字符串 flag
末尾加入额外的 ,ccs=encoding
字段来指示读取文件所用编码。
当 encoding
为 UTF-8 时,应当使用 ,ccs=utf-8
。
示例
|
|
其他相关函数
本节内容参考自中文 cppreference 的 空终止宽字符串。
限于篇幅,以下只列出常用函数。
字符串操作
函数名 | 函数原型 | 说明 |
---|---|---|
wcscpy | wchar_t *wcscpy( wchar_t *dest, const wchar_t *src ); | 将一个宽字符串复制给另一个 |
wcsncpy | wchar_t* wcsncpy( wchar_t* dest, const wchar_t* src, size_t count ); | 将一定量的宽字符从一个字符串复制到另一个 |
wcscat | wchar_t *wcscat( wchar_t *dest, const wchar_t *src ); | 将一个宽字符串的副本后附于另一个 |
wcsncat | wchar_t *wcsncat( wchar_t *dest, const wchar_t *src, size_t count ); | 将一定量宽字符串从一个宽字符串后附到另一个 |
字符串检验
函数名 | 函数原型 | 说明 |
---|---|---|
wcslen | size_t wcslen( const wchar_t *str ); | 返回宽字符串的长度 |
wcscmp | int wcscmp( const wchar_t *lhs, const wchar_t *rhs ); | 比较两个宽字符串 |
wcsncmp | int wcsncmp( const wchar_t* lhs, const wchar_t* rhs, size_t count ); | 比较来自两个宽字符串的一定量字符 |
wcsstr | wchar_t* wcsstr( const wchar_t* dest, const wchar_t* src ); | 在 dest 所指的空终止宽字符串中,寻找 src 所指的空终止宽字符串的首次出现 |
wcstok | wchar_t* wcstok( wchar_t* str, const wchar_t* delim, wchar_t **ptr ); | 寻找 str 所指向的空终止宽字符串中的下个记号。以 delim 所指向的空终止宽字符串鉴别分隔符 |
宽字符数组操作
函数名 | 函数原型 | 说明 |
---|---|---|
wmemcpy | wchar_t* wmemcpy( wchar_t* dest, const wchar_t* src, size_t count ); | 在两个不重叠的数组间复制一定数量的宽字符 |
wmemset | wchar_t *wmemset( wchar_t *dest, wchar_t ch, size_t count ); | 将给定的宽字符复制到宽字符数组的所有位置 |
locale
设置
采用 setlocale(LC_ALL, "chs");
将当前 locale
设置为中文环境即可。