2010-09-11 59 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

相關問題