在Windows上获得实际的文件名(适当外壳) [英] Getting actual file name (with proper casing) on Windows

查看:120
本文介绍了在Windows上获得实际的文件名(适当外壳)的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

Windows文件系统不区分大小写。如何给出一个文件/文件夹名称(例如somefile),我得到的的文件/文件夹的实际的名称(例如,它应该返回SomeFile如果资源管理器中显示它的话)?

Windows file system is case insensitive. How, given a file/folder name (e.g. "somefile"), I get the actual name of that file/folder (e.g. it should return "SomeFile" if Explorer displays it so)?

某些方面,我知道,所有这一切都显得相当落后:

Some ways I know, all of which seem quite backwards:


  1. 给定的完整路径,搜索(通过用FindFirstFile)路径上的每个文件夹中。这给适当的套管每个文件夹的结果。在最后一步,搜索该文件本身。

  2. 从手柄获取文件名(如 MSDN例如)。这需要打开一个文件,创建文件映射,得到它的名字,解析设备名称等pretty费解。它不会对文件夹或零大小的文件。

  1. Given the full path, search for each folder on the path (via FindFirstFile). This gives proper cased results of each folder. At the last step, search for the file itself.
  2. Get filename from handle (as in MSDN example). This requires opening a file, creating file mapping, getting it's name, parsing device names etc. Pretty convoluted. And it does not work for folders or zero-size files.

我缺少一些明显WinAPI的电话吗?最简单的,如GetActualPathName()或GetFullPathName()返回使用在通过外壳名称(例如,返回程序文件,如果被传递,即使它应该是程序文件)。

Am I missing some obvious WinAPI call? The simplest ones, like GetActualPathName() or GetFullPathName() return the name using casing that was passed in (e.g. returns "program files" if that was passed in, even if it should be "Program Files").

我在找一个本地的解决方案(而不是.NET的一个)。

I'm looking for a native solution (not .NET one).

推荐答案

和现我回答我的问题的基础上,<一href=\"http://stackoverflow.com/questions/74451/getting-actual-file-name-with-proper-casing-on-windows#74588\">original从回答的 cspirz 的。

And hereby I answer my own question, based on original answer from cspirz.

下面是给定的绝对,相对或网络路径,将与大/小写返回的路径,因为它会在Windows上显示的功能。如果路径中的某些成分不存在,它将返回路径传递从该点。

Here's a function that given absolute, relative or network path, will return the path with upper/lower case as it would be displayed on Windows. If some component of the path does not exist, it will return the passed in path from that point.

这是相当复杂,因为它试图处理网络路径和其他边缘情况。它运行在宽字符串和使用的std :: wstring的。是的,在理论上统一code TCHAR可能是不一样的wchar_t的;这是读者的练习:)

It is quite involved because it tries to handle network paths and other edge cases. It operates on wide character strings and uses std::wstring. Yes, in theory Unicode TCHAR could be not the same as wchar_t; that is an exercise for the reader :)

std::wstring GetActualPathName( const wchar_t* path )
{
    // This is quite involved, but the meat is SHGetFileInfo

    const wchar_t kSeparator = L'\\';

    // copy input string because we'll be temporary modifying it in place
    size_t length = wcslen(path);
    wchar_t buffer[MAX_PATH];
    memcpy( buffer, path, (length+1) * sizeof(path[0]) );

    size_t i = 0;

    std::wstring result;

    // for network paths (\\server\share\RestOfPath), getting the display
    // name mangles it into unusable form (e.g. "\\server\share" turns
    // into "share on server (server)"). So detect this case and just skip
    // up to two path components
    if( length >= 2 && buffer[0] == kSeparator && buffer[1] == kSeparator )
    {
        int skippedCount = 0;
        i = 2; // start after '\\'
        while( i < length && skippedCount < 2 )
        {
            if( buffer[i] == kSeparator )
                ++skippedCount;
            ++i;
        }

        result.append( buffer, i );
    }
    // for drive names, just add it uppercased
    else if( length >= 2 && buffer[1] == L':' )
    {
        result += towupper(buffer[0]);
        result += L':';
        if( length >= 3 && buffer[2] == kSeparator )
        {
            result += kSeparator;
            i = 3; // start after drive, colon and separator
        }
        else
        {
            i = 2; // start after drive and colon
        }
    }

    size_t lastComponentStart = i;
    bool addSeparator = false;

    while( i < length )
    {
        // skip until path separator
        while( i < length && buffer[i] != kSeparator )
            ++i;

        if( addSeparator )
            result += kSeparator;

        // if we found path separator, get real filename of this
        // last path name component
        bool foundSeparator = (i < length);
        buffer[i] = 0;
        SHFILEINFOW info;

        // nuke the path separator so that we get real name of current path component
        info.szDisplayName[0] = 0;
        if( SHGetFileInfoW( buffer, 0, &info, sizeof(info), SHGFI_DISPLAYNAME ) )
        {
            result += info.szDisplayName;
        }
        else
        {
            // most likely file does not exist.
            // So just append original path name component.
            result.append( buffer + lastComponentStart, i - lastComponentStart );
        }

        // restore path separator that we might have nuked before
        if( foundSeparator )
            buffer[i] = kSeparator;

        ++i;
        lastComponentStart = i;
        addSeparator = true;
    }

    return result;
}

再次感谢cspirz指着我的SHGetFileInfo。

Again, thanks to cspirz for pointing me to SHGetFileInfo.

这篇关于在Windows上获得实际的文件名(适当外壳)的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆