2010-09-11 71 views
18

Windows使用不区分大小写的文件名,所以我可以与任何这些打开相同的文件:在Python中,如何获取文件的正确路径?

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

等。鉴于这些路径的,我怎么能找到真正的情况?我想他们都产生:

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

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

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

+1

看起来这是http://stackoverflow.com/questions/2113822/python-getting-filename-case的DUP存储在窗口,它有答案。 – 2010-09-11 20:03:05

回答

6

这里有一个简单,STDLIB只,解决方法:

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

我喜欢这样:它会欺骗'glob'为我做'os.walk'! – 2011-08-21 11:49:09

+1

它只修复文件名,而不修复以前的子目录。我添加另一个答案,基于此http://stackoverflow.com/a/14742779/1355726 – xvorsx 2013-02-07 06:37:19

+0

这似乎并不奏效。此外,它会失败的文件名字符在他们是全局令牌。如果它确实触发了一个目录扫描,它也可能会在病态上很慢...... – 2016-05-11 07:29:10

4

由于NTFS(或VFAT)文件系统上“真实案例”的定义确实很奇怪,因此似乎最好的方法是走路径并匹配os.listdir()

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

+0

这是我害怕的非直接解决方案...... :( – 2010-09-11 19:25:10

+0

+1我的想法正好 – aaronasterling 2010-09-11 19:27:40

1

我会用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 
7

This python-win32 thread有不需要第三方软件包答案或行走的树:

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 
+1

这对我来说不起作用(在W7上测试) – Joril 2011-03-14 13:32:10

+0

它可能失败,因为'path'必须是'GetLongPathNameW '尝试用'unicode(path)'调用'GetLongPathName(path,buf,260)'中的'path'代替 – Attila 2013-03-06 13:35:09

+0

这不起作用GetLongPathName只扩展短文件名,所以如果你给它“C: \ Progra〜1“,你会得到”C:\ Program Files“,但如果你给它”C:\ PROGRAM FILES“,它已经是一个很长的路径名,所以它不会改变它。 – 2016-05-11 07:33:59

13

Ned的GetLongPathName答案不起作用(至少不适合我)。您需要拨打GetShortPathname返回值GetLongPathName。使用pywin32为了简洁(一个ctypes解决方案将类似于Ned的):

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

完美的作品,谢谢:) – Joril 2011-03-14 13:31:24

+0

请参阅我的评论http://stackoverflow.com/a/2114975/179715;如果短文件名生成被禁用,这并不能保证工作。 – jamesdlin 2016-01-16 20:50:19

+0

如果您关心完整路径,请注意这不会将驱动器号转换为更典型的大写字母。 – 2016-03-08 21:16:15

6

Ethan answer正确的只有文件名,而不是子文件夹名的道路上。 这是我的猜测:

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] 
2

我喜欢伊森和xvorsx的方法。据我所知,下面不会也害在其他平台上:

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] 
2

基于关闭一对夫妇的listdir同时的/走上面的例子,但支持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 
1

我只是用相同的挣扎问题。我不确定,但我认为以前的答案并不涵盖所有情况。我的实际问题是驱动器盘符与系统看到的不一样。这里是我的解决方案,还检查了正确的盘符外壳(使用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 (e. g. 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 
5

这一个统一,缩短和修复了几个方法: 标准库只;转换所有路径部分(驱动器号除外);相对或绝对路径;开车或不开车; 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 
+0

这是这个线程中唯一一个为我工作的人。谢谢! – MaVCArt 2016-08-17 14:03:54

相关问题