开发者

Reverse of os.path.normcase on Windows

开发者 https://www.devze.com 2022-12-13 10:09 出处:网络
Is there a simple way to get the \"real\" case sensitive path from a all lower case path. Like the reverse of os.path.normcase.

Is there a simple way to get the "real" case sensitive path from a all lower case path. Like the reverse of os.path.normcase.

For example, consider the directory:

c:\StackOverFlow

If I have the following snippet, how to obtain 开发者_Go百科d_real?

>>> import os
>>> d = os.path.normcase('C:\\StackOverFlow') # convert to lower case
>>> d
'c:\\stackoverflow'
>>> d_real = ... # should give 'C:\StackOverFlow' with the correct case


I wouldn't consider this solution simple, but what you can to is:

import os
d = os.path.normcase('C:\\StackOverFlow')
files = os.listdir(os.path.dirname(d))
for f in files:
  if not d.endswith(f.lower()):
    continue
  else
    real_d = os.path.join(os.path.dirname(d), f)

It's probably not efficient (depending on the number of files in the directory). It needs tweaking for the path-components (my solution really only corrects the case of the file name and doesn't care about the directory names). Also, maybe os.walk could be helpful to traverse down the tree.


You can do this by chaining GetShortPathName and GetLongPathName. This can in theory not work because you can disable short filenames on Windows with some configuration setting. Here's some sample code using ctypes:

def normcase(path):
    import ctypes
    GetShortPathName = ctypes.windll.kernel32.GetShortPathNameA
    GetLongPathName = ctypes.windll.kernel32.GetLongPathNameA
    # First convert path to a short path
    short_length = GetShortPathName(path, None, 0)
    if short_length == 0:
        return path
    short_buf = ctypes.create_string_buffer(short_length)
    GetShortPathName(path, short_buf, short_length)
    # Next convert the short path back to a long path
    long_length = GetLongPathName(short_buf, None, 0)
    long_buf = ctypes.create_string_buffer(long_length)
    GetLongPathName(short_buf, long_buf, long_length)
    return long_buf.value


Using standard lib only, this one works on all path parts / subdirs (except drive letter):

def casedpath(path):
    r = glob.glob(re.sub(r'([^:/\\])(?=[/\\]|$)', r'[\1]', path))
    return r and r[0] or path

And this one handles UNC paths in addition:

def casedpath_unc(path):
    unc, p = os.path.splitunc(path)
    r = glob.glob(unc + re.sub(r'([^:/\\])(?=[/\\]|$)', r'[\1]', p))
    return r and r[0] or path


Dirty hack approach,

import glob
...
if os.path.exists(d):
    d_real = glob.glob(d + '*')[0][:len(d)]


Definitely ugly, but fun:

def getRealDirPath(path):
    try:
        open(path)
    except IOError, e:
        return str(e).split("'")[-2]

Of course:

  • works only with dirs
  • will be buggy if dir cannot be open for another reason

But can still be useful if you don't need it for "life or death" kind of code.

Tried to grep the standard lib to find how they found the real path but couldn't find it. Must be in C.

That was the dirty hack of the day, next time we will use a regexp on the stacktrace just because we can :-)

0

精彩评论

暂无评论...
验证码 换一张
取 消