我在使用mingw编译开发程序的时候一直坚持用ansi函数,我感觉比较方便,能与std::string,和char类型直接转换。也能支持中文,英文。
唯一需要的就是与GTK程序使用的UTF8编码互相转一下就行了。UTF-8<-->ANSI
最后我出现了问题,我是遍历目录,得到所有文件路径之后,再获取每个文件的大小,但是我扫描目录时获取到的总大小少了一些,我查找数据内容,发现一些文件的大小无法记录,是0,这些文件是:
cosplay美女 兽耳 ɡ ɭϵ 美女高清壁纸_彼岸壁纸.jpg
白鼬 冬天 ѩ 动物 壁纸_彼岸壁纸.jpg
夜晚 森林 С¹ 小女孩 唯美 壁纸_彼岸壁纸.jpg
他们的路径名字包含一些奇怪字符,ɡ ɭϵ,ѩ,С¹,所以就无法获取文件了。
我一开始是以为用的函数不对,是一个简单的这个函数直接获取大小
uintmax_t get_file_size_utf8(std::string& filePath) {
struct stat st;
if (stat(filePath.c_str(), &st) == 0) {
return st.st_size;
}
return 0; // 文件不存在
}后来我换成了用WINDOWS API获取大小,还是不行
uintmax_t get_file_size_ansi(std::string& filePath) {
WIN32_FIND_DATA findData;
HANDLE hFind = FindFirstFile(filePath.c_str(), &findData);
if (hFind == INVALID_HANDLE_VALUE) {
std::cerr << "Failed to find file: " << GetLastError() << std::endl;
return 0;
}
FindClose(hFind);
// 组合高 32 位和低 32 位
ULARGE_INTEGER fileSize;
fileSize.LowPart = findData.nFileSizeLow;
fileSize.HighPart = findData.nFileSizeHigh;
return fileSize.QuadPart;
}最后我用CreateFileA获取文件大小,还是不行。
问AI,他说我字符是unicode字符,用ansi函数会出问题。我就改了,改成用CreateFileW,但是是unicode字符函数,还是不行,参数是UTF8编码的。
uintmax_t get_file_size_utf8(std::string& filePath) {
std::wstring wpath = UTF8ToUnicode(filePath);
HANDLE hFile = CreateFileW(
wpath.c_str(),
GENERIC_READ,
FILE_SHARE_READ,
NULL,
OPEN_EXISTING,
FILE_ATTRIBUTE_NORMAL,
NULL
);
if (hFile == INVALID_HANDLE_VALUE) {
std::cerr << "Failed to open file: " << GetLastError() << std::endl;
return 0;
}
LARGE_INTEGER fileSize;
if (!GetFileSizeEx(hFile, &fileSize)) {
std::cerr << "Failed to get file size: " << GetLastError() << std::endl;
CloseHandle(hFile);
return 0;
}
CloseHandle(hFile);
return static_cast<uint64_t>(fileSize.QuadPart);
}为什么不行呢?按理说应该行,但是我发现我传入的字符参数是UTF8格式,而这个UTF8格式是从ANSI函数遍历目录获取文件路径转换得到的,而遍历目录的时候扫描带有unicode字符的文件名的时候会直接损失信息,在遍历目录的时候还是因为使用了ANSI函数导致错误。
所以不管自己之后怎么转换,把ANSI函数得到的路径字符直接转换成unicode,或者转换成utf8再转换成unicode都没有用了。再用那些办法函数获取文件大小也没有用。
就像使用文本编辑器,用ANSI格式保存文件的话,会有提示信息的一样,说明ANSI格式无法保存这些unicode字符:
我准备重写所有用了ansi函数和string的地方。变成unicode函数和wstring。之前不听ai的警告,为了方便一直把他提供的代码直接用ansi函数替代。现在吃亏。所以为了开发现代多语言程序,必须弃用windowsapi的ansi函数,使用unicode函数。
但是今天我发现,我不必把代码所有string换成wstring,不必重写所有地方。只需要把遍历目录的地方、获取文件大小,所有之前用了ANSI函数的地方换成UNICODE函数就行了,返回数据的时候,再把UNICODE编码转回GTK使用的UTF8编码就行了。并且成功的获得到了文件大小信息。GTK是UTF8编码本身是UNICODE的实现,也能够显示正常的文件名。
现在GTK和调用UNICODE的WINDOWS API就是这样的关系:UFT8<-->UNICODE
之前是这样的关系:UTF-8<-->ANSI
最后,我发现GTK2.0开发的时候使用WINDOWS API,就是要做好GTK使用的UTF-8和WINDOW API使用的UNICODE(UTF-16)的边界转换。转换好了就能够搭配使用。而WINDOW API的ANSI函数,必须弃用了。
下面,是询问AI这些编码的区别:
列举ANSI、Unicode、UTF-8、UTF-16编码的含义
以下是ANSI、Unicode、UTF-8和UTF-16编码的含义及区别的清晰对比:
1. ANSI
- 含义:
ANSI是早期Windows系统默认的字符编码标准,实际指代的是**代码页(Code Page)**体系(如CP1252、GB2312等),而非统一的编码。不同地区使用不同的代码页,例如:- 英语/西欧:
CP1252(拉丁字母) - 简体中文:
GB2312或GBK - 繁体中文:
Big5
- 英语/西欧:
- 特点:
- 单字节或双字节:部分字符用1字节(0-255),部分语言(如中文)用2字节。
- 不兼容多语言:同一文档无法混合显示多种语言的字符。
- 无统一标准:不同代码页的字符可能冲突。
2. Unicode
- 含义:
Unicode是一个字符集标准,旨在为全球所有字符分配唯一的数字编号(称为码点/Code Point),例如:A的码点:U+0041中的码点:U+4E2D😊的码点:U+1F60A
- 特点:
- 统一性:覆盖几乎所有语言和符号(最新版本包含超过14万个字符)。
- 与编码无关:仅定义字符与码点的映射,具体存储方式由UTF-8/UTF-16等实现。
3. UTF-8
- 含义:
UTF-8是Unicode的一种变长编码实现,使用1到4字节表示一个字符:- 英文字符:1字节(兼容ASCII)
- 欧洲语言:2字节
- 中文/日文:3字节
- 表情符号/生僻字:4字节
- 特点:
- 兼容ASCII:ASCII文本直接是有效的UTF-8。
- 空间高效:英文为主的文本比UTF-16更节省空间。
- 互联网主流:Web页面(HTML)、电子邮件、Linux系统默认使用。
4. UTF-16
- 含义:
UTF-16是Unicode的另一种编码,使用2或4字节表示字符:- 基本多语言平面(BMP,U+0000到U+FFFF):2字节
- 辅助平面(如表情符号):4字节(代理对机制)
- 特点:
- 定长与变长混合:大部分常用字符固定2字节。
- Windows内部使用:Windows API、Java、.NET默认采用UTF-16。
- 空间权衡:中文文本比UTF-8更紧凑,但英文体积翻倍。
关键区别总结
| 编码类型 | 性质 | 字节长度 | 主要应用场景 | 示例 |
|---|---|---|---|---|
| ANSI | 传统代码页 | 1或2字节 | 旧版Windows系统 | A(1字节,CP1252) |
| Unicode | 字符集标准 | 理论编号 | 统一字符定义 | U+4E2D = "中" |
| UTF-8 | Unicode编码 | 1~4字节 | 互联网、Unix系统、数据库 | 中 = 0xE4B8AD |
| UTF-16 | Unicode编码 | 2或4字节 | Windows系统、Java/.NET | 中 = 0x4E2D |
为什么需要多种编码?
- 历史兼容:ANSI适应早期单语言环境。
- 效率权衡:UTF-8适合多语言混合(如网页),UTF-16适合亚洲语言密集的场景。
- 平台依赖:不同系统(Windows vs Unix)选择不同默认编码。
如果需要进一步了解具体编码的二进制存储方式或转换规则,可以深入探讨!
关于ANSI、Unicode、UTF-8、UTF-16编码之间的转换,我写在了下一篇博客里面。