División multiplataforma del camino en python

Me gustaría algo que tenga el mismo efecto que este:

>>> path = "/foo/bar/baz/file" >>> path_split = path.rsplit('/')[1:] >>> path_split ['foo', 'bar', 'baz', 'file'] 

Pero eso funcionará con las rutas de Windows también. Sé que hay un os.path.split() pero eso no hace lo que quiero y no vi nada que lo haga.

El OP especificado “también funcionará con las rutas de Windows”. Hay algunas arrugas con las rutas de Windows.

En primer lugar, Windows tiene el concepto de unidades múltiples, cada una con su propio directorio de trabajo actual, y 'c:foo' y 'c:\\foo' menudo no son lo mismo. Por lo tanto, es una muy buena idea separar primero cualquier designador de unidad, utilizando os.path.splitdrive (). Luego, la drive + os.path.join(*other_pieces) puede volver a montar la ruta (si es necesario drive + os.path.join(*other_pieces)

En segundo lugar, las rutas de Windows pueden contener barras o barras diagonales inversas o una mezcla. En consecuencia, el uso de os.sep al analizar una ruta no normalizada no es útil.

Más generalmente:

Los resultados producidos para 'foo' y 'foo/' no deben ser idénticos.

La condición de terminación del bucle parece expressse mejor como “os.path.split () trató su entrada como no dividible”.

Aquí hay una solución sugerida, con pruebas, que incluye una comparación con la solución de @ Spacedman

 import os.path def os_path_split_asunder(path, debug=False): parts = [] while True: newpath, tail = os.path.split(path) if debug: print repr(path), (newpath, tail) if newpath == path: assert not tail if path: parts.append(path) break parts.append(tail) path = newpath parts.reverse() return parts def spacedman_parts(path): components = [] while True: (path,tail) = os.path.split(path) if not tail: return components components.insert(0,tail) if __name__ == "__main__": tests = [ '', 'foo', 'foo/', 'foo\\', '/foo', '\\foo', 'foo/bar', '/', 'c:', 'c:/', 'c:foo', 'c:/foo', 'c:/users/john/foo.txt', '/users/john/foo.txt', 'foo/bar/baz/loop', 'foo/bar/baz/', '//hostname/foo/bar.txt', ] for i, test in enumerate(tests): print "\nTest %d: %r" % (i, test) drive, path = os.path.splitdrive(test) print 'drive, path', repr(drive), repr(path) a = os_path_split_asunder(path) b = spacedman_parts(path) print "a ... %r" % a print "b ... %r" % b print a == b 

y aquí está la salida (Python 2.7.1, Windows 7 Pro):

 Test 0: '' drive, path '' '' a ... [] b ... [] True Test 1: 'foo' drive, path '' 'foo' a ... ['foo'] b ... ['foo'] True Test 2: 'foo/' drive, path '' 'foo/' a ... ['foo', ''] b ... [] False Test 3: 'foo\\' drive, path '' 'foo\\' a ... ['foo', ''] b ... [] False Test 4: '/foo' drive, path '' '/foo' a ... ['/', 'foo'] b ... ['foo'] False Test 5: '\\foo' drive, path '' '\\foo' a ... ['\\', 'foo'] b ... ['foo'] False Test 6: 'foo/bar' drive, path '' 'foo/bar' a ... ['foo', 'bar'] b ... ['foo', 'bar'] True Test 7: '/' drive, path '' '/' a ... ['/'] b ... [] False Test 8: 'c:' drive, path 'c:' '' a ... [] b ... [] True Test 9: 'c:/' drive, path 'c:' '/' a ... ['/'] b ... [] False Test 10: 'c:foo' drive, path 'c:' 'foo' a ... ['foo'] b ... ['foo'] True Test 11: 'c:/foo' drive, path 'c:' '/foo' a ... ['/', 'foo'] b ... ['foo'] False Test 12: 'c:/users/john/foo.txt' drive, path 'c:' '/users/john/foo.txt' a ... ['/', 'users', 'john', 'foo.txt'] b ... ['users', 'john', 'foo.txt'] False Test 13: '/users/john/foo.txt' drive, path '' '/users/john/foo.txt' a ... ['/', 'users', 'john', 'foo.txt'] b ... ['users', 'john', 'foo.txt'] False Test 14: 'foo/bar/baz/loop' drive, path '' 'foo/bar/baz/loop' a ... ['foo', 'bar', 'baz', 'loop'] b ... ['foo', 'bar', 'baz', 'loop'] True Test 15: 'foo/bar/baz/' drive, path '' 'foo/bar/baz/' a ... ['foo', 'bar', 'baz', ''] b ... [] False Test 16: '//hostname/foo/bar.txt' drive, path '' '//hostname/foo/bar.txt' a ... ['//', 'hostname', 'foo', 'bar.txt'] b ... ['hostname', 'foo', 'bar.txt'] False 

Python 3.4 introdujo un nuevo módulo pathlib . pathlib.Path proporciona métodos relacionados con el sistema de archivos, mientras que pathlib.PurePath opera completamente independiente del sistema de archivos:

 >>> from pathlib import PurePath >>> path = "/foo/bar/baz/file" >>> path_split = PurePath(path).parts >>> path_split ('\\', 'foo', 'bar', 'baz', 'file') 

Puede usar PosixPath y WindowsPath explícitamente cuando lo desee:

 >>> from pathlib import PureWindowsPath, PurePosixPath >>> PureWindowsPath(path).parts ('\\', 'foo', 'bar', 'baz', 'file') >>> PurePosixPath(path).parts ('/', 'foo', 'bar', 'baz', 'file') 

Y, por supuesto, también funciona con las rutas de Windows:

 >>> wpath = r"C:\foo\bar\baz\file" >>> PurePath(wpath).parts ('C:\\', 'foo', 'bar', 'baz', 'file') >>> PureWindowsPath(wpath).parts ('C:\\', 'foo', 'bar', 'baz', 'file') >>> PurePosixPath(wpath).parts ('C:\\foo\\bar\\baz\\file',) >>> >>> wpath = r"C:\foo/bar/baz/file" >>> PurePath(wpath).parts ('C:\\', 'foo', 'bar', 'baz', 'file') >>> PureWindowsPath(wpath).parts ('C:\\', 'foo', 'bar', 'baz', 'file') >>> PurePosixPath(wpath).parts ('C:\\foo', 'bar', 'baz', 'file') 

¡Huzzah para desarrolladores de Python mejora constantemente el lenguaje!

Alguien dijo “use os.path.split “. Esto se eliminó por desgracia, pero es la respuesta correcta.

os.path.split (ruta)

Divida la ruta de la ruta de acceso en un par (cabeza, cola) donde cola es el último componente de la ruta de acceso y la cabeza es todo lo que lleva a eso. La parte de la cola nunca contendrá una barra oblicua; Si el camino termina en una barra, la cola estará vacía. Si no hay una barra en el camino, la cabeza estará vacía. Si el camino está vacío, tanto la cabeza como la cola están vacías. Las barras inclinadas se eliminan de la cabeza a menos que sea la raíz (solo una o más barras). En todos los casos, join (head, tail) devuelve una ruta a la misma ubicación que la ruta (pero las cadenas pueden diferir).

Así que no se trata solo de dividir el nombre de archivo y el nombre de archivo. Puede aplicarlo varias veces para obtener la ruta completa de manera portátil y correcta. Ejemplo de código:

 dirname = path path_split = [] while True: dirname, leaf = split(dirname) if leaf: path_split = [leaf] + path_split #Adds one element, at the beginning of the list else: #Uncomment the following line to have also the drive, in the format "Z:\" #path_split = [dirname] + path_split break 

Por favor, acredite al autor original si esa respuesta se recupera.

Utilice la funcionalidad proporcionada en os.path , por ejemplo,

 os.path.split(path) 

Como escrito en otra parte, puedes llamarlo varias veces para dividir rutas más largas.

Utilice la funcionalidad proporcionada en os.path, por ejemplo,

 os.path.split(path) 

(Esta respuesta fue de otra persona y se eliminó de forma misteriosa e incorrecta, ya que es una respuesta práctica; si desea dividir cada parte de la ruta de acceso, puede llamarla varias veces y cada llamada sacará un componente del final). .)

Aquí hay una implementación explícita del enfoque que solo utiliza iterativamente os.path.split ; utiliza una condición de terminación de bucle ligeramente diferente a la respuesta aceptada.

 def splitpath(path): parts=[] (path, tail)=os.path.split( path) while path and tail: parts.append( tail) (path,tail)=os.path.split(path) parts.append( os.path.join(path,tail) ) return map( os.path.normpath, parts)[::-1] 

Esto debería satisfacer os.path.join( *splitpath(path) ) es la path en el sentido de que ambos indican el mismo archivo / directorio.

Probado en linux:

 In [51]: current='/home/dave/src/python' In [52]: splitpath(current) Out[52]: ['/', 'home', 'dave', 'src', 'python'] In [53]: splitpath(current[1:]) Out[53]: ['.', 'dave', 'src', 'python'] In [54]: splitpath( os.path.join(current, 'module.py')) Out[54]: ['/', 'home', 'dave', 'src', 'python', 'module.py'] In [55]: splitpath( os.path.join(current[1:], 'module.py')) Out[55]: ['.', 'dave', 'src', 'python', 'module.py'] 

ntpath mano algunas de las rutas de DOS, usando el módulo os.path con ntpath , me parece bien, pero no estoy muy familiarizado con los entresijos de las rutas de DOS.

Un bash más con la opción maxplit, que es un reemplazo para os.path.split ()

 def pathsplit(pathstr, maxsplit=1): """split relative path into list""" path = [pathstr] while True: oldpath = path[:] path[:1] = list(os.path.split(path[0])) if path[0] == '': path = path[1:] elif path[1] == '': path = path[:1] + path[2:] if path == oldpath: return path if maxsplit is not None and len(path) > maxsplit: return path 

Así que sigue usando os.path.split hasta que llegues a lo que quieres. Aquí hay una implementación fea que usa un bucle infinito:

 import os.path def parts(path): components = [] while True: (path,tail) = os.path.split(path) if tail == "": components.reverse() return components components.append(tail) 

Pegue eso en parts.py, importe partes, y listo:

 >>> parts.parts("foo/bar/baz/loop") ['foo', 'bar', 'baz', 'loop'] 

Probablemente una implementación más agradable utilizando generadores o recursión por ahí …