16

Though Windows is case insensitive, it does preserve case in filenames. In Python, is there any way to get a filename with case as it is stored on the file system?

E.g., in a Python program I have filename = "texas.txt", but want to know that it's actually stored "TEXAS.txt" on the file system, even if this is inconsequential for various file operations.

Sridhar Ratnakumar
  • 81,433
  • 63
  • 146
  • 187
diggums
  • 163
  • 1
  • 5

6 Answers6

10

Here's the simplest way to do it:

>>> import win32api
>>> win32api.GetLongPathName(win32api.GetShortPathName('texas.txt')))
'TEXAS.txt'
Sridhar Ratnakumar
  • 81,433
  • 63
  • 146
  • 187
  • 1
    Does `win32api.GetLongPathName(win32api.GetShortPathName('texas.txt'))` work? – Sridhar Ratnakumar Jan 22 '10 at 16:03
  • 2
    Yes, that works. Actually, to clarify, your original suggestion does work, but not if including a directory - e.g., `win32api.GetLongPathName('\\states\\texas.txt')` yields `'\\states\\TEXAS.txt'`, whereas `win32api.GetLongPathName(win32api.GetShortPathName('\\states\\texas.txt'))` correctly yields `'\\STATES\\TEXAS.txt'`. That had confused me, now I'm all set. Thanks! – diggums Jan 22 '10 at 16:34
  • I see. Then, I have modified my answer to call `win32api.GetShortPathName` as well. – Sridhar Ratnakumar Jan 22 '10 at 18:22
  • 3
    Great solution; to clarify: The puzzling inner `GetShortPathName()` call is needed, because `GetLongPathName()` does _not_ case-correct paths that are _already_ in long (non-8.3 format). In Python 3.x, the example works as-is even with non-ASCII filenames, but in 2.x you'll have to use Unicode strings explicitly and call `GetLongPathNameW()` (note the `W`) instead. If you have `pip`, you can install the `win32api` module (via the `pypiwin32` package) by running `pip install pypiwin32` – mklement0 Aug 13 '15 at 22:17
  • 2
    Note that this solution will *not* work if the file has no short filename (which can happen if short filename generation is disabled on the system or on the volume). Additionally, `GetLongPathName` will not correct the capitalization of the drive letter. – jamesdlin Jan 16 '16 at 20:48
  • This seems to fail in some cases, see https://github.com/thonny/thonny/issues/925 – Aivar Oct 02 '19 at 18:56
6

I had problems with special characters with the win32api solution above. For unicode filenames you need to use:

win32api.GetLongPathNameW(win32api.GetShortPathName(path))
CrouZ
  • 1,721
  • 17
  • 17
  • 2
    Good point (applies to Python 2.x - not needed on 3.x, which is natively Unicode). Just to be explicit: the input path (too) must be a Unicode string (e.g., `u'texas.txt'`). – mklement0 Aug 13 '15 at 21:48
  • See my comment to http://stackoverflow.com/a/2114975/179715 ; this isn't guaranteed to work if short filename generation is disabled. – jamesdlin Jan 16 '16 at 20:49
4

This one is standard library only and converts all path parts (except drive letter):

def casedpath(path):
    r = glob.glob(re.sub(r'([^:/\\])(?=[/\\]|$)|\[', r'[\g<0>]', 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'[\g<0>]', p))
    return r and r[0] or path

Note: It is somewhat slower than the file system dependent Win API "GetShortPathName" method, but works platform & file system independent and also when short filename generation is switched off on Windows volumes (fsutil.exe 8dot3name query C:). The latter is recommended at least for performance critical file systems when no 16bit apps rely anymore on that:

fsutil.exe behavior set disable8dot3 1
kxr
  • 4,841
  • 1
  • 49
  • 32
  • It's also necessary to protect square brackets in paths like `copy[12]`. I'm augmenting the answer using `glob.escape` if you don't mind – robyschek Jan 03 '20 at 02:21
  • @robyschek yes, however I embedded the protection directly into the sub, so that it also supports ending `[` 's (as in `oddfilename[` , `oddfilename]`) and Py 2.7 & <3.4 – kxr Jan 03 '20 at 14:00
1
>>> import os
>>> os.listdir("./")
['FiLeNaMe.txt']

Does this answer your question?

sberry
  • 128,281
  • 18
  • 138
  • 165
1

You could use:

import os
a = os.listdir('mydirpath')
b = [f.lower() for f in a]
try:
    i = b.index('texas.txt')
    print a[i]
except ValueError:
    print('File not found in this directory')

This of course assumes that your search string 'texas.txt' is in lowercase. If it isn't you'll have to convert it to lowercase first.

Chinmay Kanchi
  • 62,729
  • 22
  • 87
  • 114
1

and if you want to recurse directories

import os
path=os.path.join("c:\\","path")
for r,d,f in os.walk(path):
    for file in f:
        if file.lower() == "texas.txt":
              print "Found: ",os.path.join( r , file )
ghostdog74
  • 327,991
  • 56
  • 259
  • 343