python windows filenames

En Python, ¿cómo puedo obtener la ruta de acceso correcta para un archivo?



title python (10)

Windows usa nombres de archivo que no distinguen entre mayúsculas y minúsculas, por lo que puedo abrir el mismo archivo con cualquiera de estos:

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

etc. Dado cualquiera de estos caminos, ¿cómo puedo encontrar el caso verdadero? Quiero que todos produzcan:

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

os.path.normcase no lo hace, simplemente hace minúsculas. os.path.abspath devuelve una ruta absoluta, pero cada una de ellas ya es absoluta, por lo que no cambia ninguna de ellas. os.path.realpath solo se utiliza para resolver enlaces simbólicos, que Windows no tiene, por lo que es lo mismo que abspath en Windows.

¿Hay una manera sencilla de hacer esto?


Éste unifica, acorta y corrige varios enfoques: solo biblioteca estándar; convierte todas las partes de ruta (excepto la letra de unidad); caminos relativos o absolutos; letra de unidad o no; tolarante

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

Y éste maneja caminos UNC además:

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


Aquí hay una solución simple, solo estándar,

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


Basado en un par de los ejemplos listdir / walk anteriores, pero admite rutas 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


Dado que la definición de "caso real" en los sistemas de archivos NTFS (o VFAT) es realmente extraña, parece que la mejor manera sería recorrer la ruta y os.listdir() con os.listdir() .

Sí, esto parece una solución artificial, pero también lo son las rutas NTFS. No tengo una máquina de DOS para probar esto.


La respuesta de GetLongPathName de Ned no funciona del todo (al menos no para mí). GetLongPathName llamar a GetLongPathName en el valor de retorno de GetShortPathname . Usando pywin32 por brevedad (una solución de ctypes sería similar a la de Ned):

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


Prefiero el enfoque de Ethan y xvorsx. AFAIK, lo siguiente tampoco dañaría en otras plataformas:

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]


Solo estaba luchando con el mismo problema. No estoy seguro, pero creo que las respuestas anteriores no cubren todos los casos. Mi problema real fue que la letra de la unidad de disco era diferente a la que ve el sistema. Aquí está mi solución que también comprueba la carcasa correcta de la letra de la unidad (utilizando 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://.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


Este hilo de python-win32 tiene una respuesta que no requiere paquetes de terceros o caminar por el árbol:

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


os.walk , pero creo que para diskw con muchos directorios puede llevar mucho tiempo:

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


Ethan responde solo el nombre de archivo correcto, no los nombres de las subcarpetas en la ruta. Aquí está mi conjetura:

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]