C++ 对于非ASCII字符,GetFileAttributeW失败

C++ 对于非ASCII字符,GetFileAttributeW失败,c++,file,wchar-t,wstring,kernel32,C++,File,Wchar T,Wstring,Kernel32,因此,我试图检查给定的文件是否存在。下面的答案我尝试了GetFileAttributesW。对于任何ascii输入,它都可以正常工作,但对于ß、ü和á(以及我怀疑的任何其他非ascii字符),它都会失败。我得到了ERROR\u FILE\u NOT\u FOUND的文件名和ERROR\u PATH\u NOT\u FOUND的路径名,如果它们不存在,人们会想到这一点 我百分之百地确信他们做到了。我花了15分钟来复制文件名以避免输入错误,并使用文字来避免任何错误的输入。我没有发现任何错误 由于所有

因此,我试图检查给定的文件是否存在。下面的答案我尝试了
GetFileAttributesW
。对于任何ascii输入,它都可以正常工作,但对于ß、ü和á(以及我怀疑的任何其他非ascii字符),它都会失败。我得到了
ERROR\u FILE\u NOT\u FOUND
的文件名和
ERROR\u PATH\u NOT\u FOUND
的路径名,如果它们不存在,人们会想到这一点

我百分之百地确信他们做到了。我花了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");
    }
}
#包括
#包括
#包括
#包括“Windows.h”
void main(){
while(true){
std::wstring文件路径;
std::getline(std::wcin,文件路径);
DWORD dwAttrib=GetFileAttributesW(file_path.data());
if(dwAttrib==无效的文件属性){
printf(“错误:%d\n”,GetLastError());
继续;
}
if(!(dwAttrib&文件属性目录))
printf(“有效!\n”);
其他的
printf(“无效!\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;
}
在控制台窗口中输入
fooß.txt
,您可能会发现它不起作用。如果你查看调试器,你会发现应该是
ß
的字符是另一个字符。对我来说,这是
á
,但如果您的控制台代码页是其他内容,则可能会有所不同

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


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

使用文字来避免任何错误的输入。-这是否意味着您在源代码中键入了字符?我认为这不是一个好主意,因为您不知道编译器对该字符文字做了什么。首先编写一个小的
FindFirstFile/NextFile
程序,看看你得到了什么?然后取返回的flle名称,并对该名称调用
GetFileAttributes
stdio.h
和friends是传统的C兼容头-使用
cstdio
和friends。以这种方式尝试(使用常量Unicode字符串)
DWORD dwAttrib=GetFileAttributesW(L“C:\\dir\\yourßßß文件”)。如果有效,问题在于
wstring
转换或
getline
@PaulMcKenzie“不知道编译器对该字符文字做了什么”:当然,你知道。使用选定的特定字符编码作为源代码,将其告知编译器,并告知编译器要将其转换为哪种编码。(见此。)即使使用编译器的默认值,这也不是一个选项。@PaulMcKenzie我通过使用建议的
FindFirstFile/NextFile
方法得到了有问题的文件名。应该是/是“ß”的字符是“▀" 在输出中(我希望它可以显示在网站上),这看起来不像是一般的“我不知道”,但它是一个特定的unicode字符。如果我使用这个文件名作为我程序的输入,一切都会正常工作。我仍然不知道为什么这是一个不同于预期的角色。谢谢你的回答!我用BOM表保存了这个文件,它成功了。我发现我可以通过向编译器传递
-utf-8
标志来获得相同的效果,这不需要我用BOM表再次保存每个文件。尽管这可能是可取的。控制台中没有unicode是一个不幸的限制,但这不是一个大问题,所以我可能会忽略它。我的程序在某个时候会有一个GUI,所以我可以添加unicode支持。谢谢
    std::wstring file_path;
    std::getline(std::wcin, file_path);