c++ - GetFileAttributeW 对非 ASCII 字符失败

标签 c++ file wchar-t wstring kernel32

所以我正在尝试检查给定文件是否存在。正在关注this回答 我试过 GetFileAttributesW。它适用于任何 ascii 输入,但不适用于 ß、ü 和 á(以及我怀疑的任何其他非 ascii 字符)。我得到 ERROR_FILE_NOT_FOUND 用于文件名和 ERROR_PATH_NOT_FOUND 用于路径名,如果它们不存在,人们会期望。

我 100% 确定他们做到了。我花了 15 分钟来复制文件名以避免拼写错误,并使用文字来避免任何错误的输入。我找不到任何错误。

因为所有这些字符都是非 ascii 字符,所以我停止尝试,因为我怀疑我可能搞砸了编码。我就是看不出来。有什么我想念的吗?我链接到 Kernel32.lib

谢谢!

#include <stdio.h>
#include <iostream>
#include <string>
#include "Windows.h"


void main(){
    while(true){
        std::wstring file_path;
        std::getline(std::wcin, file_path);

        DWORD dwAttrib = GetFileAttributesW(file_path.data());
        if(dwAttrib == INVALID_FILE_ATTRIBUTES){
            printf("error: %d\n", GetLastError());
            continue;
        }

        if(!(dwAttrib & FILE_ATTRIBUTE_DIRECTORY))
            printf("valid!\n");
        else
            printf("invalid!\n");
    }
}

最佳答案

要使 Unicode 在 Windows 的控制台程序中很好地工作是非常困难的,所以让我们从删除它的那个方面开始(现在)。

修改你的程序,让它看起来像这样:

#include <cstdio>
#include <iostream>
#include <string>
#include "Windows.h"

int main() {
    std::wstring file_path = L"fooß.txt";

    DWORD dwAttrib = GetFileAttributesW(file_path.data());
    if (dwAttrib == INVALID_FILE_ATTRIBUTES)
        printf("error: %d\n", GetLastError());

    if (!(dwAttrib & FILE_ATTRIBUTE_DIRECTORY))
        printf("valid!\n");
    else
        printf("invalid!\n");

    return 0;
}

即使您使用的是 UTF-8,也请确保使用字节顺序标记 (BOM) 保存此文件。 Windows 应用程序,包括 Visual Studio 和编译器,可能对此非常挑剔。如果您的编辑器不这样做,请使用 Visual Studio 编辑文件,然后使用“另存为”,单击“保存”按钮旁边的向下箭头,选择“使用编码”。在“高级保存选项”对话框中,选择“Unicode(带签名的 UTF-8)- 代码页 65001”。

确保当前文件夹中有一个名为 fooß.txt 的文件。我强烈建议使用 GUI 程序创建此文件,例如记事本或资源管理器。

这个程序有效。如果您仍然收到找不到文件的消息,请检查以确保临时文件在工作目录中或更改程序以使用绝对路径。如果您使用绝对路径,请使用反斜杠并确保它们都已正确转义。检查拼写错误、扩展名等。这段代码确实有效。

现在,如果您从标准输入中获取文件名:

    std::wstring file_path;
    std::getline(std::wcin, file_path);

然后您在控制台窗口中输入fooß.txt,您可能会发现它不起作用。如果您查看调试器,您会发现应该是 ß 的字符是其他字符。对我来说,它是 á,但如果您的控制台代码页是其他东西,它对您来说可能会有所不同。

ß 在 Unicode 中是 U+00DF。在 Windows 1252(美国 Windows 用户最常用的代码页)中,它是 0xDF,因此看起来似乎不可能出现转换问题。但是控制台窗口(默认情况下)使用 OEM 代码页。在美国,常见的 OEM 代码页是 437。因此,当我尝试在控制台中键入 ß 时,它实际上被编码为 0xE1。惊喜!这与 á 的 Unicode 值相同。如果您设法输入一个值为 0xDF 的字符,您将看到它对应于您在原始问题中报告的 block 字符。

您会认为(好吧, 会认为)从 std::wcin 请求输入会进行任何必要的转换。但事实并非如此,这可能有一些遗留的向后兼容性原因。您可以尝试使用“适当的”代码页来填充流,但这会变得很复杂,而且我从来没有费心去尝试让它工作。我只是停止尝试在控制台上使用 ASCII 以外的任何东西。

关于c++ - GetFileAttributeW 对非 ASCII 字符失败,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/46962348/

相关文章:

c++ - 一遍又一遍地重新定义变量是否会增加内存?

c++ - 如何测试字符串是否包含C++中的任何数字

c++ - 两个数组之间的数据聚类和比较

file - 使用 7zip 按年份归档文件

c++ - 打印 wchar_t 到控制台的内存泄漏问题?

c++ - odeint 简单的 1d ode 示例无法编译

java - 读取 jar 内的文件?

PHP正则表达式匹配多个条目?

c++ - getcwd 有 wchar_t 版本吗?

C++ wchart_t 警告 : character constant too long for its type