GetFileAttributeW对于非ASCII字符

时间:2017-10-26 19:29:30

标签: 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");
    }
}

1 个答案:

答案 0 :(得分:3)

在Windows上的控制台程序中使Unicode运行起来非常困难,所以让我们从删除它的方面开始(暂时)。

修改程序,使其如下所示:

#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编辑该文件,然后使用“另存为”,单击“保存”按钮旁边的向下箭头,选择“使用编码”。在“高级保存选项”对话框中,选择&#34; Unicode(带签名的UTF-8) - 代码页65001&#34;。

确保当前文件夹中有一个名为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的字符,您将看到该字符对应于您在原始问题中报告的块字符。

你会认为(好吧,会认为)要求来自std::wcin的输入会做任何必要的转换。但它没有,并且可能存在一些传统的向后兼容性原因。您可以尝试使用&#34;适当的&#34;代码页,但这变得很复杂,我从来没有费心去尝试让它工作。我只是停止尝试在控制台上使用除ASCII以外的任何东西。