2010-02-06 28 views
118

Tengo un fondo C++/Obj-C y estoy descubriendo Python (lo he estado escribiendo durante aproximadamente una hora). Estoy escribiendo un script para leer recursivamente el contenido de los archivos de texto en una estructura de carpetas.Carpeta recursiva de Python lea

El problema que tengo es que el código que he escrito solo funcionará para una carpeta de profundidad. Puedo ver por qué en el código (ver #hardcoded path), simplemente no sé cómo puedo avanzar con Python ya que mi experiencia con este es solo nueva.

Código Python:

import os 
import sys 

rootdir = sys.argv[1] 

for root, subFolders, files in os.walk(rootdir): 

    for folder in subFolders: 
     outfileName = rootdir + "/" + folder + "/py-outfile.txt" # hardcoded path 
     folderOut = open(outfileName, 'w') 
     print "outfileName is " + outfileName 

     for file in files: 
      filePath = rootdir + '/' + file 
      f = open(filePath, 'r') 
      toWrite = f.read() 
      print "Writing '" + toWrite + "' to" + filePath 
      folderOut.write(toWrite) 
      f.close() 

     folderOut.close() 

Respuesta

221

Asegúrese de que comprende los tres valores de retorno de os.walk:

for root, subdirs, files in os.walk(rootdir): 

tiene el siguiente significado:

  • root: ruta actual que se "recorrió"
  • subdirs: Los archivos en el directorio de root tipo
  • files: Archivos en root (no en subdirs) del tipo que no sea el directorio

Y por favor utilice os.path.join en lugar de la concatenación con una barra! Su problema es filePath = rootdir + '/' + file - debe concatenar la carpeta actualmente "caminada" en lugar de la carpeta superior. Entonces eso debe ser filePath = os.path.join(root, file). Por cierto, el "archivo" es un comando incorporado, por lo que normalmente no lo usas como nombre de variable.

Otro problema son los bucles, que debe ser así, por ejemplo:

import os 
import sys 

walk_dir = sys.argv[1] 

print('walk_dir = ' + walk_dir) 

# If your current working directory may change during script execution, it's recommended to 
# immediately convert program arguments to an absolute path. Then the variable root below will 
# be an absolute path as well. Example: 
# walk_dir = os.path.abspath(walk_dir) 
print('walk_dir (absolute) = ' + os.path.abspath(walk_dir)) 

for root, subdirs, files in os.walk(walk_dir): 
    print('--\nroot = ' + root) 
    list_file_path = os.path.join(root, 'my-directory-list.txt') 
    print('list_file_path = ' + list_file_path) 

    with open(list_file_path, 'wb') as list_file: 
     for subdir in subdirs: 
      print('\t- subdirectory ' + subdir) 

     for filename in files: 
      file_path = os.path.join(root, filename) 

      print('\t- file %s (full path: %s)' % (filename, file_path)) 

      with open(file_path, 'rb') as f: 
       f_content = f.read() 
       list_file.write(('The file %s contains:\n' % filename).encode('utf-8')) 
       list_file.write(f_content) 
       list_file.write(b'\n') 

Si no lo sabía, la declaración with de archivos es una abreviatura:

with open('filename', 'rb') as f: 
    dosomething() 

# is effectively the same as 

f = open('filename', 'rb') 
try: 
    dosomething() 
finally: 
    f.close() 
+4

Excelente, muchas impresiones para entender lo que está pasando y funciona perfectamente. ¡Gracias! +1 –

+8

Dirige a cualquiera tan tonto/inconsciente como yo ... este ejemplo de código escribe un archivo txt en cada directorio. Me alegro de que lo haya probado en una carpeta con la versión controlada, aunque todo lo que necesito para escribir una secuencia de comandos de limpieza está aquí también :) – Steazy

0

creo que el problema es que no se está procesando la salida del os.walk correctamente.

En primer lugar, el cambio:

filePath = rootdir + '/' + file 

a:

filePath = root + '/' + file 

rootdir es su directorio de inicio fija; root es un directorio devuelto por os.walk.

En segundo lugar, no necesita sangrar el bucle de procesamiento de archivos, ya que no tiene sentido ejecutar esto para cada subdirectorio. Obtendrá root establecido para cada subdirectorio. No necesita procesar los subdirectorios a mano a menos que desee hacer algo con los directorios.

+0

tengo datos de cada subdirectorio, por lo que necesita tener un archivo de texto separado para el contenido de cada directorio. –

+0

@Brock: la parte de archivos es la lista de archivos en el directorio actual. Entonces la sangría es realmente incorrecta. Está escribiendo en 'filePath = rootdir + '/' + file', que no suena bien: el archivo pertenece a la lista de archivos actuales, por lo que está escribiendo en muchos archivos existentes. –

2

uso os.path.join() para construir sus caminos - Es más ordenado:

import os 
import sys 
rootdir = sys.argv[1] 
for root, subFolders, files in os.walk(rootdir): 
    for folder in subFolders: 
     outfileName = os.path.join(root,folder,"py-outfile.txt") 
     folderOut = open(outfileName, 'w') 
     print "outfileName is " + outfileName 
     for file in files: 
      filePath = os.path.join(root,file) 
      toWrite = open(filePath).read() 
      print "Writing '" + toWrite + "' to" + filePath 
      folderOut.write(toWrite) 
     folderOut.close() 
+0

Parece que este código funciona solo para carpetas de 2 niveles (o más profundo). Aún así me acerca más. –

23

acuerdo con Dave Webb, os.walk producirá un elemento para cada directorio en el árbol. El hecho es que simplemente no tiene que preocuparse por subFolders.

código como esto debería funcionar:

import os 
import sys 

rootdir = sys.argv[1] 

for folder, subs, files in os.walk(rootdir): 
    with open(os.path.join(folder, 'python-outfile.txt'), 'w') as dest: 
     for filename in files: 
      with open(os.path.join(folder, filename), 'r') as src: 
       dest.write(src.read()) 
+1

Bueno. Esto funciona también. Sin embargo, prefiero la versión de AndiDog aunque es más larga porque es más fácil de entender como principiante de Python. +1 –

0

os.walk ¿Camina recursiva por defecto. Para cada dir, a partir de la raíz que produce un 3-tupla (DirPath, dirnames, nombres de archivos)

from os import walk 
from os.path import splitext, join 

def select_files(root, files): 
    """ 
    simple logic here to filter out interesting files 
    .py files in this example 
    """ 

    selected_files = [] 

    for file in files: 
     #do concatenation here to get full path 
     full_path = join(root, file) 
     ext = splitext(file)[1] 

     if ext == ".py": 
      selected_files.append(full_path) 

    return selected_files 

def build_recursive_dir_tree(path): 
    """ 
    path - where to begin folder scan 
    """ 
    selected_files = [] 

    for root, dirs, files in walk(path): 
     selected_files += select_files(root, files) 

    return selected_files 
+1

En Python 2.6 'walk()' ** do ** return recursive list. Probé tu código y obtuve una lista con muchas repeticiones ... Si solo eliminas líneas con el comentario "# llamadas recursivas en subcarpetas" - funciona bien – borisbn

+0

@borisbn tienes razón, ¡gracias! – b1r3k

0

Prueba esto:

import os 
import sys 

for root, subdirs, files in os.walk(path): 

    for file in os.listdir(root): 

     filePath = os.path.join(root, file) 

     if os.path.isdir(filePath): 
      pass 

     else: 
      f = open (filePath, 'r') 
      # Do Stuff 
13

Si está utilizando Python 3.5 + o superior, puede obtener esto hecho en 1 linea

for filename in glob.iglob(root_dir + '**/*.txt', recursive=True): 
    print(filename) 

Como se ha mencionado in documentation

If recursive is true, the pattern '**' will match any files and zero or more directories and subdirectories.

Si desea todos los archivos, puede utilizar

for filename in glob.iglob(root_dir + '**/*', recursive=True): 
    print(filename) 
+0

TypeError: iglob() obtuvo un argumento de palabra clave inesperado 'recursivo' – Jewenile

+0

Como se mencionó al principio, es solo para Python 3.5+ – ChillarAnand

+0

Sí, no he notado que mientras tengo 3.5+ ya, el intérprete bash no lo hace. Perdón por molestar – Jewenile

Cuestiones relacionadas