因为坚持使用ANSI函数导致的问题--ANSI,UNICODE,UTF8的编码格式

⌚Time: 2025-06-02 23:55:00

👨‍💻Author: Jack Ge

我在使用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


2. Unicode


3. UTF-8


4. UTF-16


关键区别总结

编码类型 性质 字节长度 主要应用场景 示例
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、Unicode、UTF-8、UTF-16编码之间的转换,我写在了下一篇博客里面。