1. 统计中文文本的字符个数
使用宽字符相关函数 wcslen() 以及宽字符文件读取相关函数 fgetws() 统计中文文本的字符个数。
需要注意的是,设定 GBK 编码的代码在不同系统下不同。windows 系统下 设定 GBK 编码的代码为 setlocale(LC_ALL, "chs"),其他系统(Linux,Mac)下设定 GBK 编码的代码为 setlocale(LC_ALL, "zh_CN.gbk")。设定 GBK 编码后,读取文件的代码是一致的,均为 file = fopen(filename, "r")。
UTF-8 正好与之相反。不同系统下设定 UTF-8 的代码相同,均为 setlocale(LC_ALL, "zh_CN.utf-8"),而读取 UTF-8 编码文件的代码不一致。windows 系统下读取 utf-8 编码的文件代码为 file = fopen(filename, "r, ccs=utf-8"),其他系统均为 file = fopen(filename, "r")。
#include <stdio.h>
#include <locale.h>
#include <wchar.h>
// 设定错误类型
#define ERROR_ILLEGAL_FILENAME -1
#define ERROR_CANNOT_OPEN_FILE -2
#define ERROR_READ_FILE -3
#define ERROR_UNSUPPORTED_CHARSET -99
#define CHARSET_UTF8 0
#define CHARSET_GBK 1
#define BUFFER_SIZE 512
int CountCharactersInFile(char const *filename, int charset) {
if (filename == NULL) return ERROR_ILLEGAL_FILENAME;
FILE *file;
// 不同编码格式,执行不同操作
switch (charset) {
case CHARSET_GBK:
#ifdef _WIN32 // 设定不同系统对应的 GBK 编码
setlocale(LC_ALL, "chs");
#else
setlocale(LC_ALL, "zh_CN.gbk");
#endif
file = fopen(filename, "r");
break;
case CHARSET_UTF8:
setlocale(LC_ALL, "zh_CN.utf-8"); // 不同系统下设定 utf-8 方法相同
#ifdef _WIN32 // 不同系统使用 utf-8 格式,读取的方法有差别
file = fopen(filename, "r, ccs=utf-8");
#else
file = fopen(filename, "r");
#endif
break;
default: return ERROR_UNSUPPORTED_CHARSET;
}
// 文件打开失败
if(file == NULL) return ERROR_CANNOT_OPEN_FILE;
// 文件打开成功
wchar_t wcs[BUFFER_SIZE];
int count = 0;
while(fgetws(wcs, BUFFER_SIZE, file) != NULL){ // 文件没读完
count += wcslen(wcs); // 读取的字符个数加和
}
// 文件读完,判断读完的原因
if(ferror(file)){
perror("method error!");
fclose(file); // 文件打开就要关闭,注意!!
return ERROR_READ_FILE;
}
fclose(file);
return count;
}
int main() {
TestWchar();
printf("data/sanguo_gbk.txt: %d\n", CountCharactersInFile("data/sanguo_gbk.txt", CHARSET_GBK));
printf("data/sanguo_utf8.txt: %d\n", CountCharactersInFile("data/sanguo_utf8.txt", CHARSET_UTF8));
printf("data/CMakeLists_gbk.txt: %d\n", CountCharactersInFile("data/CMakeLists_gbk.txt", CHARSET_GBK));
printf("data/CMakeLists_utf8.txt: %d\n", CountCharactersInFile("data/CMakeLists_utf8.txt", CHARSET_UTF8));
return 0;
}
2. 统计英文字符个数
统计英文个数可以使用窄字符相关函数 strlen() 和 fgets(),也可使用上述统计中文字符个数的代码(原因?)。
mingw 编译器下(未修改编译器编码格式),GBK 和 UTF-8 编码的程序文件,其执行结果相同。msvc 编译器 + 程序文件 GBK 编码(编译器默认编码格式为 GBK)也可得到下图的结果,而当 msvc 编译器 + 文件 UTF-8 编码时,若不修改编译器编码格式为 UTF-8 时,则会报程序错误。
故一定要注意三个地方的编码需要保持一致。一,文件本身的编码;二,编译器编译时使用的编码;三,程序代码读取相关字符时使用的编码。三者编码保持一致,才可以正确获取宽字符的个数。三个地方的编码调整方式见:https://blog.csdn.net/gltzlike/article/details/119479343。
此外,使用 wcslen() 函数时,如果三个地方编码格式不一致,不能获取到正确的宽字符个数。
void TestWchar(){
wchar_t str1[] = L"你好,中国!";
int count = wcslen(str1);
printf("%d", count); // 6
}
3. 疑惑
1)为什么统计中文字符个数的代码,可以准确无误的统计英文字符个数?