在Python中,如何获得文件的正确path?

Windows使用不区分大小写的文件名,所以我可以用以下任何一个打开相同的文件:

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

鉴于这些path,我怎么能find真实的情况? 我希望他们都生产:

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

os.path.normcase不这样做,它只是降低一切。 os.path.abspath返回一个绝对path,但是每一个都是绝对path,所以它不会改变它们中的任何一个。 os.path.realpath仅用于parsingWindows没有的符号链接,所以它与Windows上的abspath相同。

有没有一个简单的方法来做到这一点?

Solutions Collecting From Web of "在Python中,如何获得文件的正确path?"

以下是一个简单的stdlib解决方案:

 import glob def get_actual_filename(name): name = "%s[%s]" % (name[:-1], name[-1]) return glob.glob(name)[0] 

内德的GetLongPathName答案不起作用(至少不是我)。 您需要在GetShortPathname的返回值上调用GetLongPathName 。 使用pywin32简洁(ctypes解决方案看起来类似于Ned的):

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

这个python-win32线程有一个不需要第三方包或走树的答案:

 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 

Ethan只回答正确的文件名,而不是路径上的子文件夹名称。 这是我的猜测:

 def get_actual_filename(name): dirs = name.split('\\') # disk letter test_name = [dirs[0].upper()] for d in dirs[1:]: test_name += ["%s[%s]" % (d[:-1], d[-1])] res = glob.glob('\\'.join(test_name)) if not res: #File not found return None return res[0] 

这个统一,缩短和修复了几种方法:只有标准的lib; 转换所有路径部分(驱动器号除外); 相对或绝对路径; 开车或不开车; tolarant:

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

另外这个处理UNC路径:

 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 

由于NTFS(或VFAT)文件系统上的“真实案例”的定义确实是离奇的,所以最好的办法是沿着路径行进,并与os.listdir()匹配。

是的,这似乎是一个人为的解决方案,但NTFS路径也是如此。 我没有一台DOS机器来测试这个。

我更喜欢Ethan和xvorsx的方法。 AFAIK,以下不会对其他平台造成危害:

 import os.path from glob import glob def get_actual_filename(name): sep = os.path.sep parts = os.path.normpath(name).split(sep) dirs = parts[0:-1] filename = parts[-1] if dirs[0] == os.path.splitdrive(name)[0]: test_name = [dirs[0].upper()] else: test_name = [sep + dirs[0]] for d in dirs[1:]: test_name += ["%s[%s]" % (d[:-1], d[-1])] path = glob(sep.join(test_name))[0] res = glob(sep.join((path, filename))) if not res: #File not found return None return res[0] 

基于上面的几个listdir / walk示例,但支持UNC路径

 def get_actual_filename(path): orig_path = path path = os.path.normpath(path) # Build root to start searching from. Different for unc paths. if path.startswith(r'\\'): path = path.lstrip(r'\\') path_split = path.split('\\') # listdir doesn't work on just the machine name if len(path_split) < 3: return orig_path test_path = r'\\{}\{}'.format(path_split[0], path_split[1]) start = 2 else: path_split = path.split('\\') test_path = path_split[0] + '\\' start = 1 for i in range(start, len(path_split)): part = path_split[i] if os.path.isdir(test_path): for name in os.listdir(test_path): if name.lower() == part.lower(): part = name break test_path = os.path.join(test_path, part) else: return orig_path return test_path 

我会使用os.walk ,但是我认为对于具有许多目录的diskw来说,这可能是非常耗时的:

 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 

我只是同样的问题挣扎。 我不确定,但我认为以前的答案不包括所有的情况。 我的实际问题是驱动器盘符与系统看到的不一样。 这是我的解决方案,也检查正确的驱动器号(使用win32api)的外壳:

  def get_case_sensitive_path(path): """ Get case sensitive path based on not - case sensitive path. Returns: The real absolute path. Exceptions: ValueError if the path doesn't exist. Important note on Windows: when starting command line using letter cases different from the actual casing of the files / directories, the interpreter will use the invalid cases in path (eg os.getcwd() returns path that has cases different from actuals). When using tools that are case - sensitive, this will cause a problem. Below code is used to get path with exact the same casing as the actual. See http://stackoverflow.com/questions/2113822/python-getting-filename-case-as-stored-in-windows """ drive, path = os.path.splitdrive(os.path.abspath(path)) path = path.lstrip(os.sep) path = path.rstrip(os.sep) folders = [] # Make sure the drive number is also in the correct casing. drives = win32api.GetLogicalDriveStrings() drives = drives.split("\000")[:-1] # Get the list of the the form C:, d:, E: etc. drives = [d.replace("\\", "") for d in drives] # Now get a lower case version for comparison. drives_l = [d.lower() for d in drives] # Find the index of matching item. idx = drives_l.index(drive.lower()) # Get the drive letter with the correct casing. drive = drives[idx] # Divide path into components. while 1: path, folder = os.path.split(path) if folder != "": folders.append(folder) else: if path != "": folders.append(path) break # Restore their original order. folders.reverse() if len(folders) > 0: retval = drive + os.sep for folder in folders: found = False for item in os.listdir(retval): if item.lower() == folder.lower(): found = True retval = os.path.join(retval, item) break if not found: raise ValueError("Path not found: '{0}'".format(retval)) else: retval = drive + os.sep return retval