Iterowanie po katalogach w Pythonie

157

Muszę iterować po podkatalogach danego katalogu i szukać plików. Jeśli otrzymam plik, muszę go otworzyć, zmienić zawartość i zastąpić go własnymi wierszami.

Próbowałem tego:

import os

rootdir ='C:/Users/sid/Desktop/test'

for subdir, dirs, files in os.walk(rootdir):
    for file in files:
        f=open(file,'r')
        lines=f.readlines()
        f.close()
        f=open(file,'w')
        for line in lines:
            newline = "No you are not"
            f.write(newline)
        f.close()

ale pojawia się błąd. Co ja robię źle?

Wilk
źródło
12
„Błąd” - jakiś konkretny błąd?
Daniel Roseman,
1
Czy mógłbyś wyjaśnić trochę o tym, co masz nadzieję zrobić z plikami / katalogami, gdy przewodnik po nich działa zgodnie z zamierzeniami? Podaj również szczegóły błędu.
ChrisProsser,
1
Komunikat o błędzie, który otrzymuję, jest taki, że nie znaleziono pliku cool.txt. W moim folderze testowym mam inny folder o nazwie src, aw folderze src mam inny folder o nazwie main, w tym folderze mam cool.txt
Wolf
4
czy możesz po prostu napisać błąd w pytaniu? jej niezmiernie irytujące i niepotrzebne jest czytanie komentarzy, aby je znaleźć.
Charlie Parker
1
ponad rok później nie mogę uwierzyć, że wróciłem do prośby o opublikowanie błędu? @Wolf
Charlie Parker

Odpowiedzi:

300

Rzeczywisty spacer po katalogach działa tak, jak został zakodowany. Jeśli zastąpisz zawartość wewnętrznej pętli prostą printinstrukcją, zobaczysz, że każdy plik został znaleziony:

import os
rootdir = 'C:/Users/sid/Desktop/test'

for subdir, dirs, files in os.walk(rootdir):
    for file in files:
        print os.path.join(subdir, file)

Jeśli nadal pojawiają się błędy podczas wykonywania powyższych czynności, podaj komunikat o błędzie.


Zaktualizowano dla Python3

import os
rootdir = 'C:/Users/sid/Desktop/test'

for subdir, dirs, files in os.walk(rootdir):
    for file in files:
        print(os.path.join(subdir, file))
ChrisProsser
źródło
1
C: / Users / sid / Desktop / test \ src \ app / cool.txt C: / Users / sid / Desktop / test \ src \ app / woohoo.txt Tak w otwartej instrukcji mojego kodu, myślę, że muszę podaj bezwzględną ścieżkę do pliku. import os rootdir = 'C: / Users / spemmara / Desktop / test / src / app /' for subdir, dirs, files in os.walk (rootdir): for file in files: f = open (subdir + '/' + file , 'r') lines = f.readlines () f.close () f = open (subdir + '/' + file, 'w') for line in lines: newline = "hey, know" f.write (newline) f.close () Dzięki stary. To rozwiązane
Wolf
3
Cześć! Należy pamiętać, że "print" w Pythonie 3 wymaga nawiasów, w przeciwnym razie zwraca błąd składni. Mam nadzieję że to pomoże!
Tommaso Di Noto
14

Innym sposobem powrocie wszystkie pliki w podkatalogach jest skorzystanie z pathlibmodułu , wprowadzony w Pythonie 3.4, który zapewnia zorientowanego podejścia obiektowego do obsługi ścieżek systemu plików (Pathlib jest również dostępny na Pythona 2.7 za pośrednictwem modułu pathlib2 na PyPI ):

from pathlib import Path

rootdir = Path('C:/Users/sid/Desktop/test')
# Return a list of regular files only, not directories
file_list = [f for f in rootdir.glob('**/*') if f.is_file()]

# For absolute paths instead of relative the current dir
file_list = [f for f in rootdir.resolve().glob('**/*') if f.is_file()]

Od Pythona 3.5, globmoduł obsługuje również rekurencyjne wyszukiwanie plików:

import os
from glob import iglob

rootdir_glob = 'C:/Users/sid/Desktop/test/**/*' # Note the added asterisks
# This will return absolute paths
file_list = [f for f in iglob('**/*', recursive=True) if os.path.isfile(f)]

Z file_listjednego z powyższych podejść można iterować bez potrzeby zagnieżdżonej pętli:

for f in file_list:
    print(f) # Replace with desired operations
joelostblom
źródło
1
Co jest lepsze w przypadku Pythona 3.6?
PhoenixDev
@PhoenixDev Generalnie nie słyszałem o zalecaniu jednego podejścia nad drugim. Wolę używać pathlibsiebie, głównie dlatego, że lubię składnię metod obiektowych. Istnieją inne różnice, na przykład biblioteka ścieżek zwraca określone klasy ścieżek zamiast ciągów znaków, a dostępne funkcje różnią się między bibliotekami (np. os.path.expanduser('~')Vs Path.home()). Przejrzyj dokumentację i zobacz, które podejście preferujesz.
joelostblom
Zamiast dodawać **wzorzec glob, możesz użyć rglob.
Georgy
12

Od 2020 roku glob.iglob(path/**, recursive=True)wydaje się najbardziej pytonicznym rozwiązaniem, czyli:

import glob, os

for filename in glob.iglob('/pardadox-music/**', recursive=True):
    if os.path.isfile(filename): # filter dirs
        print(filename)

Wynik:

/pardadox-music/modules/her1.mod
/pardadox-music/modules/her2.mod
...

Uwagi:
1 - glob.iglob

glob.iglob(pathname, recursive=False)

Zwraca iterator, który zwraca te same wartości, co glob()bez faktycznego przechowywania ich wszystkich jednocześnie.

2 - Jeśli rekurencja to True, wzorzec '**'będzie pasował do wszystkich plików i zera lub więcej directoriesi subdirectories.

3 - Jeśli katalog zawiera pliki zaczynające się od,  .nie będą one domyślnie dopasowywane. Na przykład rozważmy katalog zawierający  card.gif i .card.gif:

>>> import glob
>>> glob.glob('*.gif') ['card.gif'] 
>>> glob.glob('.c*')['.card.gif']

4 - Możesz również użyć rglob(pattern), co jest tym samym, co wywołanie  glob() z **/dodaniem przed podanym wzorcem względnym.

CONvid19
źródło
1
To pythonowe rozwiązanie nie wyświetla ukrytych plików (tzw. Dotfiles), podczas gdy akceptowany tak.
ashrasmun
@ashrasmun To, o czym wspomniałeś, jest dobrze wyjaśnione w docs.python.org/3/library/glob.html
CONvid19,