Python: getting filename case as stored in Windows?
Asked Answered
G

6

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.

Gideon answered 21/1, 2010 at 23:30 Comment(1)
(accidental duplicate accounts merged)Unexampled
P
10

Here's the simplest way to do it:

>>> import win32api
>>> win32api.GetLongPathName(win32api.GetShortPathName('texas.txt')))
'TEXAS.txt'
Portion answered 22/1, 2010 at 4:33 Comment(6)
Does win32api.GetLongPathName(win32api.GetShortPathName('texas.txt')) work?Portion
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!Gideon
I see. Then, I have modified my answer to call win32api.GetShortPathName as well.Portion
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 pypiwin32Northbound
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.Outbrave
This seems to fail in some cases, see github.com/thonny/thonny/issues/925Macey
R
6

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

win32api.GetLongPathNameW(win32api.GetShortPathName(path))
Radioactive answered 1/10, 2011 at 19:33 Comment(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').Northbound
See my comment to https://mcmap.net/q/454340/-python-getting-filename-case-as-stored-in-windows ; this isn't guaranteed to work if short filename generation is disabled.Outbrave
H
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
Hildy answered 25/8, 2016 at 8:44 Comment(2)
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 mindLashkar
@Lashkar 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.4Hildy
S
1
>>> import os
>>> os.listdir("./")
['FiLeNaMe.txt']

Does this answer your question?

Separatist answered 21/1, 2010 at 23:48 Comment(0)
M
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.

Mowbray answered 21/1, 2010 at 23:50 Comment(5)
print ([f for f in os.listdir("mydirpath") if f.lower() == "texas.txt"]+["file not found"])[0]Flita
@Roger: Haha, great stuff! I'm still getting to grips with the power of list comprehensions. I tend not to post one-liners for answers though, since they can often be more confusing than illuminating :).Mowbray
Just a note: No need to use string.lower() anymore.Kephart
The except block functionally does nothing -- thus ignoring errors silently.Portion
@Chinmay: look closely at the statements in your except block (i.e, the last line of your program). It does nothing.Portion
K
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 )
Kephart answered 22/1, 2010 at 0:48 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.