在Windows上,stat和GetFileAttributes对包含奇怪字符的路径失败

时间:2017-07-18 00:30:27

标签: c++ windows

下面的代码演示了当路径包含一些奇怪(但有效)的ASCII字符时stat和GetFileAttributes如何失败。 作为一种解决方法,我会使用8.3 DOS文件名。但是,当驱动器禁用8.3名称时,这不起作用。 (使用fsutil命令禁用8.3名称:fsutil behavior set disable8dot3 1)。

在这种情况下是否可以获取stat和/或GetFileAttributes? 如果没有,是否有另一种方法来确定路径是目录还是文件?

#include "stdafx.h"

#include <sys/stat.h>
#include <string>
#include <Windows.h>
#include <atlpath.h>

std::wstring s2ws(const std::string& s)
{
    int len;
    int slength = (int)s.length() + 1;
    len = MultiByteToWideChar(CP_ACP, 0, s.c_str(), slength, 0, 0);
    wchar_t* buf = new wchar_t[len];
    MultiByteToWideChar(CP_ACP, 0, s.c_str(), slength, buf, len);
    std::wstring r(buf);
    delete[] buf;
    return r;
}

// The final characters in the path below are 0xc3 (Ã) and 0x3f (?).
// Create a test directory with the name à and set TEST_DIR below to your test directory.
const char* TEST_DIR = "D:\\tmp\\VisualStudio\\TestProject\\ConsoleApplication1\\test_data\\Ã"; 

int main()
{
    std::string testDir = TEST_DIR;

    // test stat and _wstat
    struct stat st;
    const auto statSucceeded = stat(testDir.c_str(), &st) == 0;
    if (!statSucceeded)
    {
        printf("stat failed\n");
    }

    std::wstring testDirW = s2ws(testDir);

    struct _stat64i32 stW;
    const auto statSucceededW = _wstat(testDirW.data(), &stW) == 0;
    if (!statSucceededW)
    {
        printf("_wstat failed\n");
    }

    // test PathIsDirectory
    const auto isDir = PathIsDirectory(testDirW.c_str()) != 0;
    if (!isDir)
    {
        printf("PathIsDirectory failed\n");
    }

    // test GetFileAttributes
    const auto fileAttributes = ::GetFileAttributes(testDirW.c_str());
    const auto getFileAttributesWSucceeded = fileAttributes != INVALID_FILE_ATTRIBUTES;
    if (!getFileAttributesWSucceeded)
    {
        printf("GetFileAttributes failed\n");
    }

    return 0;
}

2 个答案:

答案 0 :(得分:1)

您遇到的问题来自使用MultiByteToWideChar功能。使用CP_ACP可以默认为不支持某些字符的代码页。如果将默认系统代码页更改为UTF8,则代码将起作用。由于您无法告诉您的客户端要使用哪个代码页,因此您可以使用第三方库(例如International Components for Unicode)将主机代码页转换为UTF16。

我使用控制台代码页65001和VS2015运行您的代码,您的代码按照书面形式运行。我还添加了正面的printfs来验证它是否正常工作。

答案 1 :(得分:1)

不要以窄字符串文字开头并尝试转换它,以宽字符串文字开头 - 表示实际文件名。您可以使用十六进制转义序列来避免对源代码的编码的任何依赖。

如果实际代码不使用字符串文字,最佳分辨率取决于具体情况;例如,如果正在从文件中读取文件名,则需要确保知道该文件的编码并相应地执行转换。

如果实际代码从命令行参数中读取文件名,则可以使用wmain()而不是main()将参数作为宽字符串获取。