GetFileAttributeW 对非 ASCII 字符失败

GetFileAttributeW fails for non-ASCII characters

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

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

因为所有这些字符都是非 ascii 字符,所以我停止尝试,因为我怀疑我可能搞砸了编码。我就是看不出来。有什么我想念的吗?我 link 反对 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;
}

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

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

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

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

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

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

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

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