views:

133

answers:

4

Windows uses case-insensitive file names, so I can open the same file with any of these:

r"c:\windows\system32\desktop.ini"
r"C:\WINdows\System32\DESKTOP.ini"
r"C:\WiNdOwS\SyStEm32\DeSkToP.iNi"

etc. Given any of these paths, how can I find the true case? I want them all to produce:

r"C:\Windows\System32\desktop.ini"

os.path.normcase doesn't do it, it simply lowercases everything. os.path.abspath returns an absolute path, but each of these is already absolute, and so it doesn't change any of them. os.path.realpath is only used to resolve symbolic links, which Windows doesn't have, so it's the same as abspath on Windows.

Is there a straightforward way to do this?

+2  A: 

Since the definition of "true case" on NTFS (or VFAT) filesystems is truly bizarre, it seems the best way would be to walk the path and match against os.listdir().

Yes, this seems like a contrived solution but so are NTFS paths. I don't have a DOS machine to test this on.

msw
This is the non-straightforward solution I was afraid of... :(
Ned Batchelder
+1 my thoughts exactly
aaronasterling
A: 

I would use os.walk, but I think that for diskw with many directories it may be time consuming:

fname = "g:\\miCHal\\ZzZ.tXt"
if not os.path.exists(fname):
    print('No such file')
else:
    d, f = os.path.split(fname)
    dl = d.lower()
    fl = f.lower()
    for root, dirs, files in os.walk('g:\\'):
        if root.lower() == dl:
            fn = [n for n in files if n.lower() == fl][0]
            print(os.path.join(root, fn))
            break
Michał Niklas
+3  A: 

This python-win32 thread has an answer that doesn't require third-party packages or walking the tree:

import ctypes

def getLongPathName(path):
    buf = ctypes.create_unicode_buffer(260)
    GetLongPathName = ctypes.windll.kernel32.GetLongPathNameW
    rv = GetLongPathName(path, buf, 260)
    if rv == 0 or rv > 260:
        return path
    else:
        return buf.value
Ned Batchelder
+1  A: 

Ned's GetLongPathName answer doesn't quite work (at least not for me). You need to call GetLongPathName on the return value of GetShortPathname. Using pywin32 for brevity (a ctypes solution would look similar to Ned's):

>>> win32api.GetLongPathName(win32api.GetShortPathName('stopservices.vbs'))
'StopServices.vbs'
Paul Moore