Jak wyświetlić listę wszystkich plików katalogu?

3473

Jak mogę wyświetlić listę wszystkich plików katalogu w Pythonie i dodać je do pliku list?

duhhunjonn
źródło
27
Powiązane z Jak uzyskać listę podkatalogów
rds

Odpowiedzi:

4207

os.listdir()dostaniesz wszystko, co znajduje się w katalogu - pliki i katalogi .

Jeśli chcesz tylko pliki, możesz to przefiltrować, używając os.path:

from os import listdir
from os.path import isfile, join
onlyfiles = [f for f in listdir(mypath) if isfile(join(mypath, f))]

lub można użyć os.walk()która otrzymując dwie listy dla każdego katalogu wizyt it - podział na plikach i katalogów dla ciebie. Jeśli chcesz tylko najwyższy katalog, możesz po prostu złamać go za pierwszym razem

from os import walk

f = []
for (dirpath, dirnames, filenames) in walk(mypath):
    f.extend(filenames)
    break
pycruft
źródło
87
(_, _, filenames) = walk(mypath).next()
Troszkę
9
Nieznaczna modyfikacja do przechowywania pełnych ścieżek: for (ścieżka, nazwy, nazwy plików) w os.walk (mypath): checksum_files.extend (os.path.join (ścieżka / nazwa pliku) dla nazwy pliku w nazwie pliku) przerwa
okigan
150
f.extend(filenames)nie jest tak naprawdę równoważne z f = f + filenames. extendzmodyfikuje fw miejscu, natomiast dodanie tworzy nową listę w nowej lokalizacji pamięci. Oznacza to na extendogół bardziej efektywną niż +, ale czasami może prowadzić do zamieszania, jeśli wiele obiektów zawiera odniesienia do listy. Na koniec warto zauważyć, że f += filenamesjest to równoważne f.extend(filenames), a nie f = f + filenames .
Benjamin Hodgson
30
@misterbee, twoje rozwiązanie jest najlepsze, tylko jedna mała poprawka:_, _, filenames = next(walk(mypath), (None, None, []))
bgusach
35
w użyciu w Pythonie 3.x(_, _, filenames) = next(os.walk(mypath))
ET-CS
1677

Wolę używać globmodułu, ponieważ dopasowuje wzorce i rozszerza.

import glob
print(glob.glob("/home/adam/*.txt"))

Zwróci listę z zapytanymi plikami:

['/home/adam/file1.txt', '/home/adam/file2.txt', .... ]
Adam
źródło
17
to skrót do listdir + fnmatch docs.python.org/library/fnmatch.html#fnmatch.fnmatch
Stefano
31
dla wyjaśnienia, nie zwraca to „pełnej ścieżki”; po prostu zwraca ekspansję globu, cokolwiek by to nie było. Na przykład, /home/user/foo/bar/hello.txtjeśli zostanie uruchomiony w katalogu foo, glob("bar/*.txt")zwróci bar/hello.txt. Zdarzają się sytuacje, w których naprawdę potrzebujesz pełnej (tj. Absolutnej) ścieżki; w takich przypadkach zobacz stackoverflow.com/questions/51520/…
michael
1
Powiązane: znajduj pliki rekurencyjnie za pomocą glob: stackoverflow.com/a/2186565/4561887
Gabriel Staples
6
nie odpowiada na to pytanie. glob.glob("*")by.
Jean-François Fabre
piękny!!!! więc .... x=glob.glob("../train/*.png")poda mi tablicę moich ścieżek, o ile znam nazwę folderu. Ale fajnie!
Jennifer Crosby
856

Uzyskaj listę plików w Pythonie 2 i 3


os.listdir()

Jak uzyskać wszystkie pliki (i katalogi) w bieżącym katalogu (Python 3)

Poniżej przedstawiono proste metody pobierania tylko plików z bieżącego katalogu, przy użyciu os i listdir()funkcji w Pythonie 3. Dalsza eksploracja pokaże, jak zwrócić foldery w katalogu, ale nie będziesz mieć pliku w podkatalogu, ponieważ można użyć marszu - omówiono później).

 import os
 arr = os.listdir()
 print(arr)

 >>> ['$RECYCLE.BIN', 'work.txt', '3ebooks.txt', 'documents']

glob

Glob był łatwiejszy do wybrania pliku tego samego typu lub z czymś wspólnym. Spójrz na następujący przykład:

import glob

txtfiles = []
for file in glob.glob("*.txt"):
    txtfiles.append(file)

glob ze zrozumieniem listy

import glob

mylist = [f for f in glob.glob("*.txt")]

glob z funkcją

Funkcja zwraca listę danego rozszerzenia (.txt, .docx ecc.) W argumencie

import glob

def filebrowser(ext=""):
    "Returns files with an extension"
    return [f for f in glob.glob(f"*{ext}")]

x = filebrowser(".txt")
print(x)

>>> ['example.txt', 'fb.txt', 'intro.txt', 'help.txt']

glob rozszerzenie poprzedniego kodu

Funkcja zwraca teraz listę plików pasujących do ciągu przekazywanego jako argument

import glob

def filesearch(word=""):
    """Returns a list with all files with the word/extension in it"""
    file = []
    for f in glob.glob("*"):
        if word[0] == ".":
            if f.endswith(word):
                file.append(f)
                return file
        elif word in f:
            file.append(f)
            return file
    return file

lookfor = "example", ".py"
for w in lookfor:
    print(f"{w:10} found => {filesearch(w)}")

wynik

example    found => []
.py        found => ['search.py']

Uzyskiwanie pełnej nazwy ścieżki za pomocą os.path.abspath

Jak zauważyłeś, w powyższym kodzie nie masz pełnej ścieżki do pliku. Jeśli potrzebujesz mieć ścieżkę bezwzględną, możesz użyć innej funkcji os.pathmodułu o nazwie _getfullpathname, umieszczając plik, który otrzymujesz os.listdir()jako argument. Istnieją inne sposoby uzyskania pełnej ścieżki, co sprawdzimy później (zastąpiłem, jak sugeruje mexmex, _getfullpathname na abspath).

 import os
 files_path = [os.path.abspath(x) for x in os.listdir()]
 print(files_path)

 >>> ['F:\\documenti\applications.txt', 'F:\\documenti\collections.txt']

Uzyskaj pełną nazwę ścieżki typu pliku do wszystkich podkatalogów za pomocą walk

Uważam, że jest to bardzo przydatne do znajdowania rzeczy w wielu katalogach i pomogło mi znaleźć plik, o którym nie pamiętałem nazwy:

import os

# Getting the current work directory (cwd)
thisdir = os.getcwd()

# r=root, d=directories, f = files
for r, d, f in os.walk(thisdir):
    for file in f:
        if file.endswith(".docx"):
            print(os.path.join(r, file))

os.listdir(): pobierz pliki w bieżącym katalogu (Python 2)

W Pythonie 2, jeśli chcesz listę plików w bieżącym katalogu, musisz podać argument jako „.” lub os.getcwd () w metodzie os.listdir.

 import os
 arr = os.listdir('.')
 print(arr)

 >>> ['$RECYCLE.BIN', 'work.txt', '3ebooks.txt', 'documents']

Aby przejść w górę w drzewie katalogów

# Method 1
x = os.listdir('..')

# Method 2
x= os.listdir('/')

Pobierz pliki: os.listdir()w określonym katalogu (Python 2 i 3)

 import os
 arr = os.listdir('F:\\python')
 print(arr)

 >>> ['$RECYCLE.BIN', 'work.txt', '3ebooks.txt', 'documents']

Pobierz pliki z konkretnego podkatalogu za pomocą os.listdir()

import os

x = os.listdir("./content")

os.walk('.') - bieżący katalog

 import os
 arr = next(os.walk('.'))[2]
 print(arr)

 >>> ['5bs_Turismo1.pdf', '5bs_Turismo1.pptx', 'esperienza.txt']

next(os.walk('.')) i os.path.join('dir', 'file')

 import os
 arr = []
 for d,r,f in next(os.walk("F:\\_python")):
     for file in f:
         arr.append(os.path.join(r,file))

 for f in arr:
     print(files)

>>> F:\\_python\\dict_class.py
>>> F:\\_python\\programmi.txt

next(os.walk('F:\\') - uzyskać pełną ścieżkę - zrozumienie listy

 [os.path.join(r,file) for r,d,f in next(os.walk("F:\\_python")) for file in f]

 >>> ['F:\\_python\\dict_class.py', 'F:\\_python\\programmi.txt']

os.walk - uzyskaj pełną ścieżkę - wszystkie pliki w podkatalogach **

x = [os.path.join(r,file) for r,d,f in os.walk("F:\\_python") for file in f]
print(x)

>>> ['F:\\_python\\dict.py', 'F:\\_python\\progr.txt', 'F:\\_python\\readl.py']

os.listdir() - pobierz tylko pliki txt

 arr_txt = [x for x in os.listdir() if x.endswith(".txt")]
 print(arr_txt)

 >>> ['work.txt', '3ebooks.txt']

Użycie, globaby uzyskać pełną ścieżkę do plików

Jeśli potrzebuję bezwzględnej ścieżki do plików:

from path import path
from glob import glob
x = [path(f).abspath() for f in glob("F:\\*.txt")]
for f in x:
    print(f)

>>> F:\acquistionline.txt
>>> F:\acquisti_2018.txt
>>> F:\bootstrap_jquery_ecc.txt

Używanie os.path.isfiledo unikania katalogów na liście

import os.path
listOfFiles = [f for f in os.listdir() if os.path.isfile(f)]
print(listOfFiles)

>>> ['a simple game.py', 'data.txt', 'decorator.py']

Korzystanie pathlibz Python 3.4

import pathlib

flist = []
for p in pathlib.Path('.').iterdir():
    if p.is_file():
        print(p)
        flist.append(p)

 >>> error.PNG
 >>> exemaker.bat
 >>> guiprova.mp3
 >>> setup.py
 >>> speak_gui2.py
 >>> thumb.PNG

Z list comprehension:

flist = [p for p in pathlib.Path('.').iterdir() if p.is_file()]

Alternatywnie użyj pathlib.Path()zamiastpathlib.Path(".")

Użyj metody glob w pathlib.Path ()

import pathlib

py = pathlib.Path().glob("*.py")
for file in py:
    print(file)

>>> stack_overflow_list.py
>>> stack_overflow_list_tkinter.py

Pobierz wszystkie i tylko pliki z os.walk

import os
x = [i[2] for i in os.walk('.')]
y=[]
for t in x:
    for f in t:
        y.append(f)
print(y)

>>> ['append_to_list.py', 'data.txt', 'data1.txt', 'data2.txt', 'data_180617', 'os_walk.py', 'READ2.py', 'read_data.py', 'somma_defaltdic.py', 'substitute_words.py', 'sum_data.py', 'data.txt', 'data1.txt', 'data_180617']

Pobierz tylko pliki z next i przejdź do katalogu

 import os
 x = next(os.walk('F://python'))[2]
 print(x)

 >>> ['calculator.bat','calculator.py']

Zdobądź tylko katalogi z next i przejdź do katalogu

 import os
 next(os.walk('F://python'))[1] # for the current dir use ('.')

 >>> ['python3','others']

Uzyskaj wszystkie nazwy podkatalogów za pomocą walk

for r,d,f in os.walk("F:\\_python"):
    for dirs in d:
        print(dirs)

>>> .vscode
>>> pyexcel
>>> pyschool.py
>>> subtitles
>>> _metaprogramming
>>> .ipynb_checkpoints

os.scandir() z Python 3.5 i nowszych

import os
x = [f.name for f in os.scandir() if f.is_file()]
print(x)

>>> ['calculator.bat','calculator.py']

# Another example with scandir (a little variation from docs.python.org)
# This one is more efficient than os.listdir.
# In this case, it shows the files only in the current directory
# where the script is executed.

import os
with os.scandir() as i:
    for entry in i:
        if entry.is_file():
            print(entry.name)

>>> ebookmaker.py
>>> error.PNG
>>> exemaker.bat
>>> guiprova.mp3
>>> setup.py
>>> speakgui4.py
>>> speak_gui2.py
>>> speak_gui3.py
>>> thumb.PNG

Przykłady:

Dawny. 1: Ile plików jest w podkatalogach?

W tym przykładzie szukamy liczby plików zawartych we wszystkich katalogach i ich podkatalogach.

import os

def count(dir, counter=0):
    "returns number of files in dir and subdirs"
    for pack in os.walk(dir):
        for f in pack[2]:
            counter += 1
    return dir + " : " + str(counter) + "files"

print(count("F:\\python"))

>>> 'F:\\\python' : 12057 files'

Przykład 2: Jak skopiować wszystkie pliki z katalogu do innego?

Skrypt służący do porządkowania na komputerze znajdujący wszystkie pliki danego typu (domyślnie: pptx) i kopiujący je w nowym folderze.

import os
import shutil
from path import path

destination = "F:\\file_copied"
# os.makedirs(destination)

def copyfile(dir, filetype='pptx', counter=0):
    "Searches for pptx (or other - pptx is the default) files and copies them"
    for pack in os.walk(dir):
        for f in pack[2]:
            if f.endswith(filetype):
                fullpath = pack[0] + "\\" + f
                print(fullpath)
                shutil.copy(fullpath, destination)
                counter += 1
    if counter > 0:
        print('-' * 30)
        print("\t==> Found in: `" + dir + "` : " + str(counter) + " files\n")

for dir in os.listdir():
    "searches for folders that starts with `_`"
    if dir[0] == '_':
        # copyfile(dir, filetype='pdf')
        copyfile(dir, filetype='txt')


>>> _compiti18\Compito Contabilità 1\conti.txt
>>> _compiti18\Compito Contabilità 1\modula4.txt
>>> _compiti18\Compito Contabilità 1\moduloa4.txt
>>> ------------------------
>>> ==> Found in: `_compiti18` : 3 files

Dawny. 3: Jak uzyskać wszystkie pliki w pliku txt

Jeśli chcesz utworzyć plik txt ze wszystkimi nazwami plików:

import os
mylist = ""
with open("filelist.txt", "w", encoding="utf-8") as file:
    for eachfile in os.listdir():
        mylist += eachfile + "\n"
    file.write(mylist)

Przykład: txt ze wszystkimi plikami na dysku twardym

"""
We are going to save a txt file with all the files in your directory.
We will use the function walk()
"""

import os

# see all the methods of os
# print(*dir(os), sep=", ")
listafile = []
percorso = []
with open("lista_file.txt", "w", encoding='utf-8') as testo:
    for root, dirs, files in os.walk("D:\\"):
        for file in files:
            listafile.append(file)
            percorso.append(root + "\\" + file)
            testo.write(file + "\n")
listafile.sort()
print("N. of files", len(listafile))
with open("lista_file_ordinata.txt", "w", encoding="utf-8") as testo_ordinato:
    for file in listafile:
        testo_ordinato.write(file + "\n")

with open("percorso.txt", "w", encoding="utf-8") as file_percorso:
    for file in percorso:
        file_percorso.write(file + "\n")

os.system("lista_file.txt")
os.system("lista_file_ordinata.txt")
os.system("percorso.txt")

Cały plik C: \ w jednym pliku tekstowym

To jest krótsza wersja poprzedniego kodu. Zmień folder, w którym chcesz rozpocząć wyszukiwanie plików, jeśli chcesz zacząć od innej pozycji. Ten kod generuje 50 MB pliku tekstowego na moim komputerze z czymś mniejszym niż 500 000 linii z plikami z pełną ścieżką.

import os

with open("file.txt", "w", encoding="utf-8") as filewrite:
    for r, d, f in os.walk("C:\\"):
        for file in f:
            filewrite.write(f"{r + file}\n")

Jak napisać plik ze wszystkimi ścieżkami w folderze typu

Za pomocą tej funkcji możesz utworzyć plik txt, który będzie miał nazwę typu pliku, którego szukasz (np. Pngfile.txt) z pełną pełną ścieżką wszystkich plików tego typu. Myślę, że czasem może się przydać.

import os

def searchfiles(extension='.ttf', folder='H:\\'):
    "Create a txt file with all the file of a type"
    with open(extension[1:] + "file.txt", "w", encoding="utf-8") as filewrite:
        for r, d, f in os.walk(folder):
            for file in f:
                if file.endswith(extension):
                    filewrite.write(f"{r + file}\n")

# looking for png file (fonts) in the hard disk H:\
searchfiles('.png', 'H:\\')

>>> H:\4bs_18\Dolphins5.png
>>> H:\4bs_18\Dolphins6.png
>>> H:\4bs_18\Dolphins7.png
>>> H:\5_18\marketing html\assets\imageslogo2.png
>>> H:\7z001.png
>>> H:\7z002.png

(Nowe) Znajdź wszystkie pliki i otwórz je za pomocą GUI tkinter

Chciałem tylko dodać w tej 2019 roku małą aplikację do wyszukiwania wszystkich plików w katalogu i móc je otworzyć, klikając dwukrotnie nazwę pliku na liście. wprowadź opis zdjęcia tutaj

import tkinter as tk
import os

def searchfiles(extension='.txt', folder='H:\\'):
    "insert all files in the listbox"
    for r, d, f in os.walk(folder):
        for file in f:
            if file.endswith(extension):
                lb.insert(0, r + "\\" + file)

def open_file():
    os.startfile(lb.get(lb.curselection()[0]))

root = tk.Tk()
root.geometry("400x400")
bt = tk.Button(root, text="Search", command=lambda:searchfiles('.png', 'H:\\'))
bt.pack()
lb = tk.Listbox(root)
lb.pack(fill="both", expand=1)
lb.bind("<Double-Button>", lambda x: open_file())
root.mainloop()
Giovanni G. PY
źródło
13
To miszmasz zbyt wielu odpowiedzi na pytania, które nie zostały tu zadane. Warto również wyjaśnić, jakie są zastrzeżenia lub zalecane podejścia. Nie ma nic lepszego niż znajomość jednego sposobu w porównaniu do 20 sposobów na zrobienie tego samego, chyba że wiem również, który z nich jest bardziej odpowiedni do użycia.
cs95
OK, JAK NAJSZYBCIEJ Spojrzę na moją odpowiedź i postaram się uczynić ją bardziej czystą i zawierającą bardziej przydatne informacje na temat różnicy między metodami itp.
Giovanni G. PY
Nie powinieneś określać rozszerzenia pliku, sprawdzając, czy nazwa pliku zawiera podciąg. To może powodować wiele problemów. Polecam zawsze sprawdzać, czy nazwa pliku kończy się na określonym podciągu.
ni1ight
Ok, @ n1light Zmieniłem kod ...
Giovanni G. PY
811
import os
os.listdir("somedirectory")

zwróci listę wszystkich plików i katalogów w „somedirectory”.

sepp2k
źródło
11
Zwraca względną ścieżkę plików w porównaniu z pełną ścieżką zwróconą przezglob.glob
xji
22
@JIXiang: os.listdir()zawsze zwraca same nazwy plików (nie ścieżki względne). To, co glob.glob()zwraca, zależy od formatu ścieżki wzorca wejściowego.
mklement0
os.listdir () -> Zawsze wyświetla katalog i plik w podanej lokalizacji. Czy jest jakiś sposób na wyświetlenie tylko katalogu, a nie plików?
RonyA
160

Jedno-liniowe rozwiązanie do zdobycia tylko listę plików (bez podkatalogów):

filenames = next(os.walk(path))[2]

lub bezwzględne nazwy ścieżek:

paths = [os.path.join(path, fn) for fn in next(os.walk(path))[2]]
Remi
źródło
7
Tylko jedna linijka, jeśli już ją masz import os. Wydaje się mniej zwięzły niż glob()dla mnie.
ArtOfWarfare
4
problem z glob polega na tym, że folder o nazwie „coś. coś” został zwrócony przez glob („/ home / adam /*.*”)
Remi
6
W OS X jest coś takiego jak pakiet. Jest to katalog, który ogólnie powinien być traktowany jako plik (jak .tar). Czy chcesz, aby traktowano je jako plik lub katalog? Użycie glob()potraktowałoby to jako plik. Twoja metoda potraktowałaby to jak katalog.
ArtOfWarfare
132

Uzyskiwanie pełnych ścieżek plików z katalogu i wszystkich jego podkatalogów

import os

def get_filepaths(directory):
    """
    This function will generate the file names in a directory 
    tree by walking the tree either top-down or bottom-up. For each 
    directory in the tree rooted at directory top (including top itself), 
    it yields a 3-tuple (dirpath, dirnames, filenames).
    """
    file_paths = []  # List which will store all of the full filepaths.

    # Walk the tree.
    for root, directories, files in os.walk(directory):
        for filename in files:
            # Join the two strings in order to form the full filepath.
            filepath = os.path.join(root, filename)
            file_paths.append(filepath)  # Add it to the list.

    return file_paths  # Self-explanatory.

# Run the above function and store its results in a variable.   
full_file_paths = get_filepaths("/Users/johnny/Desktop/TEST")

  • Ścieżka, którą podałem w powyższej funkcji, zawierała 3 pliki - dwa z nich w katalogu głównym, a drugi w podfolderze o nazwie „SUBFOLDER”. Możesz teraz robić takie rzeczy jak:
  • print full_file_paths który wydrukuje listę:

    • ['/Users/johnny/Desktop/TEST/file1.txt', '/Users/johnny/Desktop/TEST/file2.txt', '/Users/johnny/Desktop/TEST/SUBFOLDER/file3.dat']

Jeśli chcesz, możesz otworzyć i odczytać zawartość lub skoncentrować się tylko na plikach z rozszerzeniem „.dat”, jak w poniższym kodzie:

for f in full_file_paths:
  if f.endswith(".dat"):
    print f

/Users/johnny/Desktop/TEST/SUBFOLDER/file3.dat

Jasio
źródło
To jedyna odpowiedź.
thelearner
78

Od wersji 3.4 są do tego wbudowane iteratory, które są znacznie wydajniejsze niż os.listdir():

pathlib: Nowość w wersji 3.4.

>>> import pathlib
>>> [p for p in pathlib.Path('.').iterdir() if p.is_file()]

Zgodnie z PEP 428 celem pathlibbiblioteki jest zapewnienie prostej hierarchii klas do obsługi ścieżek systemu plików i wspólnych operacji wykonywanych przez użytkowników.

os.scandir(): Nowość w wersji 3.5.

>>> import os
>>> [entry for entry in os.scandir('.') if entry.is_file()]

Zauważ, że os.walk()używa os.scandir()zamiast os.listdir()z wersji 3.5, a jego prędkość wzrosła o 2-20 razy zgodnie z PEP 471 .

Pozwól mi również polecić przeczytanie komentarza ShadowRanger poniżej.

SzieberthAdam
źródło
1
Dzięki! Myślę, że to jedyne rozwiązanie, które nie zwraca bezpośrednio list. Można użyć p.namezamiast pierwszego palternatywnie, jeśli jest to preferowane.
jeromej
1
Witamy! Wolałbym generować pathlib.Path()instancje, ponieważ mają wiele użytecznych metod, których nie chciałbym marnować. Możesz także do str(p)nich zadzwonić po nazwy ścieżek.
SzieberthAdam
6
Uwaga: os.scandirrozwiązanie będzie bardziej efektywne niż os.listdirz os.path.is_fileczekiem lub podobne, nawet jeśli potrzebujesz list(więc nie korzystają z leniwej iteracji), ponieważ os.scandirużywa API OS pod warunkiem że daje is_fileinformacje za darmo, jak to iteracje , obie strony nie per-pliku na dysku do statnich w ogóle (na Windows, DirEntrya Ci kompletne statinformacje za darmo, na systemach * NIX musi statInformacje o zaświatach is_file, is_diritp, ale DirEntrybuforuje na pierwszy statdla wygody).
ShadowRanger
1
Możesz także użyć, entry.nameaby uzyskać tylko nazwę pliku lub entry.pathuzyskać pełną ścieżkę. Nigdy więcej os.path.join () w całym miejscu.
user136036
56

Uwagi wstępne

  • Chociaż w tekście pytania istnieje wyraźne rozróżnienie między terminami plików i katalogów , niektórzy mogą argumentować, że katalogi są w rzeczywistości plikami specjalnymi
  • Stwierdzenie: „ wszystkie pliki katalogu ” można interpretować na dwa sposoby:
    1. Wszystkie bezpośrednie (poziom 1) lub potomkowie tylko
    2. Wszyscy potomkowie w całym drzewie katalogów (łącznie z tymi w podkatalogach)
  • Kiedy pytanie zostało zadane, wyobrażam sobie, że Python 2 był wersją LTS , jednak próbki kodu będą uruchamiane przez Python 3 ( .5 ) (Będę utrzymywał ich zgodność z Pythonem 2, jak to możliwe; również każdy kod należący do Python, który zamierzam opublikować, pochodzi z wersji 3.5.4 - chyba że podano inaczej). Ma to konsekwencje związane z innym słowem kluczowym w pytaniu: „ dodaj je do listy ”:

    • W wersjach wcześniejszych niż Python 2.2 sekwencje (iterowalne) były w większości reprezentowane przez listy (krotki, zbiory, ...)
    • W Pythonie 2.2 koncepcja generatora ( [Python.Wiki]: Generatory ) - dzięki uprzejmości [Python 3]: instrukcja fed ). Z biegiem czasu zaczęły pojawiać się odpowiedniki generatorów dla funkcji, które zwróciły / działały z listami
    • W Python 3 generator jest zachowaniem domyślnym
    • Nie jestem pewien, czy zwrócenie listy jest nadal obowiązkowe (lub generator też by to zrobił), ale przekazanie generatora do konstruktora listy utworzy z niego listę (a także ją wykorzysta). Poniższy przykład ilustruje różnice w [Python 3]: map ( function, iterable, ... )
    >>> import sys
    >>> sys.version
    '2.7.10 (default, Mar  8 2016, 15:02:46) [MSC v.1600 64 bit (AMD64)]'
    >>> m = map(lambda x: x, [1, 2, 3])  # Just a dummy lambda function
    >>> m, type(m)
    ([1, 2, 3], <type 'list'>)
    >>> len(m)
    3


    >>> import sys
    >>> sys.version
    '3.5.4 (v3.5.4:3f56838, Aug  8 2017, 02:17:05) [MSC v.1900 64 bit (AMD64)]'
    >>> m = map(lambda x: x, [1, 2, 3])
    >>> m, type(m)
    (<map object at 0x000001B4257342B0>, <class 'map'>)
    >>> len(m)
    Traceback (most recent call last):
      File "<stdin>", line 1, in <module>
    TypeError: object of type 'map' has no len()
    >>> lm0 = list(m)  # Build a list from the generator
    >>> lm0, type(lm0)
    ([1, 2, 3], <class 'list'>)
    >>>
    >>> lm1 = list(m)  # Build a list from the same generator
    >>> lm1, type(lm1)  # Empty list now - generator already consumed
    ([], <class 'list'>)
  • Przykłady będą oparte na katalogu o nazwie katalog_główny o następującej strukturze (ten przykład dotyczy Win , ale używam tego samego drzewa na Lnx ):

    E:\Work\Dev\StackOverflow\q003207219>tree /f "root_dir"
    Folder PATH listing for volume Work
    Volume serial number is 00000029 3655:6FED
    E:\WORK\DEV\STACKOVERFLOW\Q003207219\ROOT_DIR
    ¦   file0
    ¦   file1
    ¦
    +---dir0
    ¦   +---dir00
    ¦   ¦   ¦   file000
    ¦   ¦   ¦
    ¦   ¦   +---dir000
    ¦   ¦           file0000
    ¦   ¦
    ¦   +---dir01
    ¦   ¦       file010
    ¦   ¦       file011
    ¦   ¦
    ¦   +---dir02
    ¦       +---dir020
    ¦           +---dir0200
    +---dir1
    ¦       file10
    ¦       file11
    ¦       file12
    ¦
    +---dir2
    ¦   ¦   file20
    ¦   ¦
    ¦   +---dir20
    ¦           file200
    ¦
    +---dir3


Rozwiązania

Podejścia programowe:

  1. [Python 3]: os. listdir ( ścieżka = '.' )

    Zwraca listę zawierającą nazwy pozycji w katalogu podaną przez ścieżkę. Lista jest w dowolnej kolejności i nie zawiera wpisów specjalnych '.'oraz '..'...


    >>> import os
    >>> root_dir = "root_dir"  # Path relative to current dir (os.getcwd())
    >>>
    >>> os.listdir(root_dir)  # List all the items in root_dir
    ['dir0', 'dir1', 'dir2', 'dir3', 'file0', 'file1']
    >>>
    >>> [item for item in os.listdir(root_dir) if os.path.isfile(os.path.join(root_dir, item))]  # Filter items and only keep files (strip out directories)
    ['file0', 'file1']

    Bardziej szczegółowy przykład ( code_os_listdir.py ):

    import os
    from pprint import pformat
    
    
    def _get_dir_content(path, include_folders, recursive):
        entries = os.listdir(path)
        for entry in entries:
            entry_with_path = os.path.join(path, entry)
            if os.path.isdir(entry_with_path):
                if include_folders:
                    yield entry_with_path
                if recursive:
                    for sub_entry in _get_dir_content(entry_with_path, include_folders, recursive):
                        yield sub_entry
            else:
                yield entry_with_path
    
    
    def get_dir_content(path, include_folders=True, recursive=True, prepend_folder_name=True):
        path_len = len(path) + len(os.path.sep)
        for item in _get_dir_content(path, include_folders, recursive):
            yield item if prepend_folder_name else item[path_len:]
    
    
    def _get_dir_content_old(path, include_folders, recursive):
        entries = os.listdir(path)
        ret = list()
        for entry in entries:
            entry_with_path = os.path.join(path, entry)
            if os.path.isdir(entry_with_path):
                if include_folders:
                    ret.append(entry_with_path)
                if recursive:
                    ret.extend(_get_dir_content_old(entry_with_path, include_folders, recursive))
            else:
                ret.append(entry_with_path)
        return ret
    
    
    def get_dir_content_old(path, include_folders=True, recursive=True, prepend_folder_name=True):
        path_len = len(path) + len(os.path.sep)
        return [item if prepend_folder_name else item[path_len:] for item in _get_dir_content_old(path, include_folders, recursive)]
    
    
    def main():
        root_dir = "root_dir"
        ret0 = get_dir_content(root_dir, include_folders=True, recursive=True, prepend_folder_name=True)
        lret0 = list(ret0)
        print(ret0, len(lret0), pformat(lret0))
        ret1 = get_dir_content_old(root_dir, include_folders=False, recursive=True, prepend_folder_name=False)
        print(len(ret1), pformat(ret1))
    
    
    if __name__ == "__main__":
        main()

    Uwagi :

    • Istnieją dwie implementacje:
      • Ten, który korzysta z generatorów (oczywiście tutaj wydaje się bezużyteczny, ponieważ natychmiast przekonwertowałem wynik na listę)
      • Klasyczny (nazwy funkcji kończące się na _old )
    • Wykorzystywana jest rekurencja (aby dostać się do podkatalogów)
    • Dla każdej implementacji istnieją dwie funkcje:
      • Ten, który zaczyna się od znaku podkreślenia ( _ ): „prywatny” (nie powinien być wywoływany bezpośrednio) - to robi całą pracę
      • Publiczny (opakowanie powyżej): po prostu usuwa początkową ścieżkę (jeśli jest wymagana) ze zwróconych wpisów. To brzydka implementacja, ale to jedyny pomysł, który mógłbym w tym momencie wymyślić
    • Pod względem wydajności generatory są zazwyczaj nieco szybsze (biorąc pod uwagę zarówno tworzenie, jak i iterację) ), ale nie testowałem ich w funkcjach rekurencyjnych, a także iteruję wewnątrz funkcji nad wewnętrznymi generatorami - nie wiem, jak wydajność przyjazne jest to
    • Graj z argumentami, aby uzyskać różne wyniki


    Wyjście :

    (py35x64_test) E:\Work\Dev\StackOverflow\q003207219>"e:\Work\Dev\VEnvs\py35x64_test\Scripts\python.exe" "code_os_listdir.py"
    <generator object get_dir_content at 0x000001BDDBB3DF10> 22 ['root_dir\\dir0',
     'root_dir\\dir0\\dir00',
     'root_dir\\dir0\\dir00\\dir000',
     'root_dir\\dir0\\dir00\\dir000\\file0000',
     'root_dir\\dir0\\dir00\\file000',
     'root_dir\\dir0\\dir01',
     'root_dir\\dir0\\dir01\\file010',
     'root_dir\\dir0\\dir01\\file011',
     'root_dir\\dir0\\dir02',
     'root_dir\\dir0\\dir02\\dir020',
     'root_dir\\dir0\\dir02\\dir020\\dir0200',
     'root_dir\\dir1',
     'root_dir\\dir1\\file10',
     'root_dir\\dir1\\file11',
     'root_dir\\dir1\\file12',
     'root_dir\\dir2',
     'root_dir\\dir2\\dir20',
     'root_dir\\dir2\\dir20\\file200',
     'root_dir\\dir2\\file20',
     'root_dir\\dir3',
     'root_dir\\file0',
     'root_dir\\file1']
    11 ['dir0\\dir00\\dir000\\file0000',
     'dir0\\dir00\\file000',
     'dir0\\dir01\\file010',
     'dir0\\dir01\\file011',
     'dir1\\file10',
     'dir1\\file11',
     'dir1\\file12',
     'dir2\\dir20\\file200',
     'dir2\\file20',
     'file0',
     'file1']


  1. [Python 3]: os. scandir ( ścieżka = '.' ) ( Python 3.5 +, backport: [PyPI]: scandir )

    Zwraca iterator obiektów os.DirEntry odpowiadających pozycjom w katalogu podanym przez path . Wpisy są przedstawiane w dowolnej kolejności, a wpisy specjalne '.'i '..'nie są uwzględniane.

    Użycie scandir () zamiast listdir () może znacznie zwiększyć wydajność kodu, który również potrzebuje informacji o typie pliku lub atrybucie pliku, ponieważ obiekty os.DirEntry ujawniają te informacje, jeśli system operacyjny udostępnia je podczas skanowania katalogu. Wszystkie metody os.DirEntry mogą wykonywać wywołanie systemowe, ale is_dir () i is_file () zwykle wymagają tylko wywołania systemowego dla dowiązań symbolicznych; os.DirEntry.stat () zawsze wymaga wywołania systemowego w systemie Unix, ale wymaga tylko jednego dla dowiązań symbolicznych w systemie Windows.


    >>> import os
    >>> root_dir = os.path.join(".", "root_dir")  # Explicitly prepending current directory
    >>> root_dir
    '.\\root_dir'
    >>>
    >>> scandir_iterator = os.scandir(root_dir)
    >>> scandir_iterator
    <nt.ScandirIterator object at 0x00000268CF4BC140>
    >>> [item.path for item in scandir_iterator]
    ['.\\root_dir\\dir0', '.\\root_dir\\dir1', '.\\root_dir\\dir2', '.\\root_dir\\dir3', '.\\root_dir\\file0', '.\\root_dir\\file1']
    >>>
    >>> [item.path for item in scandir_iterator]  # Will yield an empty list as it was consumed by previous iteration (automatically performed by the list comprehension)
    []
    >>>
    >>> scandir_iterator = os.scandir(root_dir)  # Reinitialize the generator
    >>> for item in scandir_iterator :
    ...     if os.path.isfile(item.path):
    ...             print(item.name)
    ...
    file0
    file1

    Uwagi :

    • Jest podobny do os.listdir
    • Ale jest również bardziej elastyczny (i oferuje więcej funkcji), więcej Python ic (aw niektórych przypadkach szybszy)


  1. [Python 3]: os. spacer ( top, topdown = True, onerror = None, followlinks = False )

    Wygeneruj nazwy plików w drzewie katalogów, chodząc po drzewie od góry do dołu lub od dołu do góry. Dla każdego katalogu w drzewie katalogów zakorzenionej w górę (w tym szczyt sam), to daje 3-krotki ( dirpath, dirnames, filenames).


    >>> import os
    >>> root_dir = os.path.join(os.getcwd(), "root_dir")  # Specify the full path
    >>> root_dir
    'E:\\Work\\Dev\\StackOverflow\\q003207219\\root_dir'
    >>>
    >>> walk_generator = os.walk(root_dir)
    >>> root_dir_entry = next(walk_generator)  # First entry corresponds to the root dir (passed as an argument)
    >>> root_dir_entry
    ('E:\\Work\\Dev\\StackOverflow\\q003207219\\root_dir', ['dir0', 'dir1', 'dir2', 'dir3'], ['file0', 'file1'])
    >>>
    >>> root_dir_entry[1] + root_dir_entry[2]  # Display dirs and files (direct descendants) in a single list
    ['dir0', 'dir1', 'dir2', 'dir3', 'file0', 'file1']
    >>>
    >>> [os.path.join(root_dir_entry[0], item) for item in root_dir_entry[1] + root_dir_entry[2]]  # Display all the entries in the previous list by their full path
    ['E:\\Work\\Dev\\StackOverflow\\q003207219\\root_dir\\dir0', 'E:\\Work\\Dev\\StackOverflow\\q003207219\\root_dir\\dir1', 'E:\\Work\\Dev\\StackOverflow\\q003207219\\root_dir\\dir2', 'E:\\Work\\Dev\\StackOverflow\\q003207219\\root_dir\\dir3', 'E:\\Work\\Dev\\StackOverflow\\q003207219\\root_dir\\file0', 'E:\\Work\\Dev\\StackOverflow\\q003207219\\root_dir\\file1']
    >>>
    >>> for entry in walk_generator:  # Display the rest of the elements (corresponding to every subdir)
    ...     print(entry)
    ...
    ('E:\\Work\\Dev\\StackOverflow\\q003207219\\root_dir\\dir0', ['dir00', 'dir01', 'dir02'], [])
    ('E:\\Work\\Dev\\StackOverflow\\q003207219\\root_dir\\dir0\\dir00', ['dir000'], ['file000'])
    ('E:\\Work\\Dev\\StackOverflow\\q003207219\\root_dir\\dir0\\dir00\\dir000', [], ['file0000'])
    ('E:\\Work\\Dev\\StackOverflow\\q003207219\\root_dir\\dir0\\dir01', [], ['file010', 'file011'])
    ('E:\\Work\\Dev\\StackOverflow\\q003207219\\root_dir\\dir0\\dir02', ['dir020'], [])
    ('E:\\Work\\Dev\\StackOverflow\\q003207219\\root_dir\\dir0\\dir02\\dir020', ['dir0200'], [])
    ('E:\\Work\\Dev\\StackOverflow\\q003207219\\root_dir\\dir0\\dir02\\dir020\\dir0200', [], [])
    ('E:\\Work\\Dev\\StackOverflow\\q003207219\\root_dir\\dir1', [], ['file10', 'file11', 'file12'])
    ('E:\\Work\\Dev\\StackOverflow\\q003207219\\root_dir\\dir2', ['dir20'], ['file20'])
    ('E:\\Work\\Dev\\StackOverflow\\q003207219\\root_dir\\dir2\\dir20', [], ['file200'])
    ('E:\\Work\\Dev\\StackOverflow\\q003207219\\root_dir\\dir3', [], [])

    Uwagi :

    • W scenach używa os.scandir( os.listdirw starszych wersjach)
    • Wykonuje ciężkie podnoszenie, powtarzając się w podfolderach


  1. [Python 3]: glob. glob ( ścieżka, *, recursive = False ) ( [Python 3]: glob. iglob ( ścieżka, *, recursive = False ) )

    Zwraca możliwie pustą listę nazw ścieżek , które pasują do nazwy ścieżki , która musi być łańcuchem zawierającym specyfikację ścieżki. ścieżka może być bezwzględna (podobna /usr/src/Python-1.5/Makefile) lub względna (podobna ../../Tools/*/*.gif) i może zawierać symbole wieloznaczne w stylu powłoki. Zepsute dowiązania symboliczne są uwzględniane w wynikach (jak w powłoce).
    ...
    Zmieniono w wersji 3.5 : Obsługa globów rekurencyjnych za pomocą „ **”.


    >>> import glob, os
    >>> wildcard_pattern = "*"
    >>> root_dir = os.path.join("root_dir", wildcard_pattern)  # Match every file/dir name
    >>> root_dir
    'root_dir\\*'
    >>>
    >>> glob_list = glob.glob(root_dir)
    >>> glob_list
    ['root_dir\\dir0', 'root_dir\\dir1', 'root_dir\\dir2', 'root_dir\\dir3', 'root_dir\\file0', 'root_dir\\file1']
    >>>
    >>> [item.replace("root_dir" + os.path.sep, "") for item in glob_list]  # Strip the dir name and the path separator from begining
    ['dir0', 'dir1', 'dir2', 'dir3', 'file0', 'file1']
    >>>
    >>> for entry in glob.iglob(root_dir + "*", recursive=True):
    ...     print(entry)
    ...
    root_dir\
    root_dir\dir0
    root_dir\dir0\dir00
    root_dir\dir0\dir00\dir000
    root_dir\dir0\dir00\dir000\file0000
    root_dir\dir0\dir00\file000
    root_dir\dir0\dir01
    root_dir\dir0\dir01\file010
    root_dir\dir0\dir01\file011
    root_dir\dir0\dir02
    root_dir\dir0\dir02\dir020
    root_dir\dir0\dir02\dir020\dir0200
    root_dir\dir1
    root_dir\dir1\file10
    root_dir\dir1\file11
    root_dir\dir1\file12
    root_dir\dir2
    root_dir\dir2\dir20
    root_dir\dir2\dir20\file200
    root_dir\dir2\file20
    root_dir\dir3
    root_dir\file0
    root_dir\file1

    Uwagi :

    • Używa os.listdir
    • W przypadku dużych drzew (szczególnie jeśli włączona jest funkcja rekurencyjna ) preferowany jest program iglob
    • Umożliwia zaawansowane filtrowanie na podstawie nazwy (ze względu na symbol wieloznaczny)


  1. [Python 3]: ścieżka klasy. Ścieżka ( * pathsegments ) ( Python 3.4 +, backport: [PyPI]: pathlib2 )

    >>> import pathlib
    >>> root_dir = "root_dir"
    >>> root_dir_instance = pathlib.Path(root_dir)
    >>> root_dir_instance
    WindowsPath('root_dir')
    >>> root_dir_instance.name
    'root_dir'
    >>> root_dir_instance.is_dir()
    True
    >>>
    >>> [item.name for item in root_dir_instance.glob("*")]  # Wildcard searching for all direct descendants
    ['dir0', 'dir1', 'dir2', 'dir3', 'file0', 'file1']
    >>>
    >>> [os.path.join(item.parent.name, item.name) for item in root_dir_instance.glob("*") if not item.is_dir()]  # Display paths (including parent) for files only
    ['root_dir\\file0', 'root_dir\\file1']

    Uwagi :

    • To jeden ze sposobów osiągnięcia naszego celu
    • To styl OOP ścieżek obsługi
    • Oferuje wiele funkcjonalności


  1. [Python 2]: dircache.listdir (ścieżka) ( tylko Python 2 )


    def listdir(path):
        """List directory contents, using cache."""
        try:
            cached_mtime, list = cache[path]
            del cache[path]
        except KeyError:
            cached_mtime, list = -1, []
        mtime = os.stat(path).st_mtime
        if mtime != cached_mtime:
            list = os.listdir(path)
            list.sort()
        cache[path] = mtime, list
        return list


  1. [man7]: OPENDIR (3) / [man7]: READDIR (3) / [man7]: CLOSEDIR (3) przez [Python 3]: ctypes - Biblioteka funkcji obcych dla Pythona ( specyficzna dla POSIX )

    ctypes to obca biblioteka funkcji dla Pythona. Zapewnia typy danych kompatybilne z C i umożliwia wywoływanie funkcji w bibliotekach DLL lub bibliotekach udostępnionych. Można go użyć do zawinięcia tych bibliotek w czysty Python.

    code_ctypes.py :

    #!/usr/bin/env python3
    
    import sys
    from ctypes import Structure, \
        c_ulonglong, c_longlong, c_ushort, c_ubyte, c_char, c_int, \
        CDLL, POINTER, \
        create_string_buffer, get_errno, set_errno, cast
    
    
    DT_DIR = 4
    DT_REG = 8
    
    char256 = c_char * 256
    
    
    class LinuxDirent64(Structure):
        _fields_ = [
            ("d_ino", c_ulonglong),
            ("d_off", c_longlong),
            ("d_reclen", c_ushort),
            ("d_type", c_ubyte),
            ("d_name", char256),
        ]
    
    LinuxDirent64Ptr = POINTER(LinuxDirent64)
    
    libc_dll = this_process = CDLL(None, use_errno=True)
    # ALWAYS set argtypes and restype for functions, otherwise it's UB!!!
    opendir = libc_dll.opendir
    readdir = libc_dll.readdir
    closedir = libc_dll.closedir
    
    
    def get_dir_content(path):
        ret = [path, list(), list()]
        dir_stream = opendir(create_string_buffer(path.encode()))
        if (dir_stream == 0):
            print("opendir returned NULL (errno: {:d})".format(get_errno()))
            return ret
        set_errno(0)
        dirent_addr = readdir(dir_stream)
        while dirent_addr:
            dirent_ptr = cast(dirent_addr, LinuxDirent64Ptr)
            dirent = dirent_ptr.contents
            name = dirent.d_name.decode()
            if dirent.d_type & DT_DIR:
                if name not in (".", ".."):
                    ret[1].append(name)
            elif dirent.d_type & DT_REG:
                ret[2].append(name)
            dirent_addr = readdir(dir_stream)
        if get_errno():
            print("readdir returned NULL (errno: {:d})".format(get_errno()))
        closedir(dir_stream)
        return ret
    
    
    def main():
        print("{:s} on {:s}\n".format(sys.version, sys.platform))
        root_dir = "root_dir"
        entries = get_dir_content(root_dir)
        print(entries)
    
    
    if __name__ == "__main__":
        main()

    Uwagi :

    • Ładuje trzy funkcje z libc (ładowane w bieżącym procesie) i wywołuje je (po więcej szczegółów sprawdź [SO]: Jak sprawdzić, czy plik istnieje bez wyjątków? (Odpowiedź @ CristiFati) - ostatnie uwagi z punktu # 4. ). To postawiłoby to podejście bardzo blisko krawędzi Pythona / C.
    • LinuxDirent64 jest ctypes reprezentacja struct dirent64 z [Man7]: dirent.h (0P) (jak to DT_ stałymi) z moim maszyny: Ubtu 16 64 ( 4.10.0-40-rodzajowe i libc6-dev: amd64 ). W innych smakach / wersjach definicja struktury może się różnić, a jeśli tak, należy zaktualizować alias ctypes , w przeciwnym razie spowoduje to niezdefiniowane zachowanie
    • Zwraca dane w os.walkformacie. Nie zadałem sobie trudu, aby uczynić go rekurencyjnym, ale zaczynając od istniejącego kodu, byłoby to dość trywialne zadanie
    • Wszystko jest wykonalne również w Win , dane (biblioteki, funkcje, struktury, stałe, ...) różnią się


    Wyjście :

    [cfati@cfati-ubtu16x64-0:~/Work/Dev/StackOverflow/q003207219]> ./code_ctypes.py
    3.5.2 (default, Nov 12 2018, 13:43:14)
    [GCC 5.4.0 20160609] on linux
    
    ['root_dir', ['dir2', 'dir1', 'dir3', 'dir0'], ['file1', 'file0']]


  1. [ActiveState.Docs]: win32file.FindFilesW ( specyficzne dla systemu Win )

    Pobiera listę pasujących nazw plików przy użyciu interfejsu Windows Unicode API. Interfejs funkcji API FindFirstFileW / FindNextFileW / Find close.


    >>> import os, win32file, win32con
    >>> root_dir = "root_dir"
    >>> wildcard = "*"
    >>> root_dir_wildcard = os.path.join(root_dir, wildcard)
    >>> entry_list = win32file.FindFilesW(root_dir_wildcard)
    >>> len(entry_list)  # Don't display the whole content as it's too long
    8
    >>> [entry[-2] for entry in entry_list]  # Only display the entry names
    ['.', '..', 'dir0', 'dir1', 'dir2', 'dir3', 'file0', 'file1']
    >>>
    >>> [entry[-2] for entry in entry_list if entry[0] & win32con.FILE_ATTRIBUTE_DIRECTORY and entry[-2] not in (".", "..")]  # Filter entries and only display dir names (except self and parent)
    ['dir0', 'dir1', 'dir2', 'dir3']
    >>>
    >>> [os.path.join(root_dir, entry[-2]) for entry in entry_list if entry[0] & (win32con.FILE_ATTRIBUTE_NORMAL | win32con.FILE_ATTRIBUTE_ARCHIVE)]  # Only display file "full" names
    ['root_dir\\file0', 'root_dir\\file1']

    Notatki :


  1. Zainstaluj jakiś pakiet innej firmy, który załatwi sprawę
    • Najprawdopodobniej będzie polegać na jednym (lub więcej) z powyższych (może z niewielkimi dostosowaniami)


Uwagi :

  • Kod ma być przenośny (z wyjątkiem miejsc kierowanych na określony obszar - które są oznaczone) lub krzyżować:

    • platforma ( Nix , Win ,)
    • Wersja Python (2, 3,)
  • W powyższych wariantach zastosowano wiele stylów ścieżek (bezwzględne, względne), aby zilustrować fakt, że stosowane „narzędzia” są elastyczne w tym kierunku

  • os.listdiri os.scandirużyj opendir / readdir / closedir ( [MS.Docs]: FindFirstFileW function / [MS.Docs]: FindNextFileW function / [MS.Docs]: FindClose function ) (przez [GitHub]: python / cpython - (master) cpython / Modules / posixmodule.c )

  • win32file.FindFilesWużywa również tych funkcji ( specyficznych dla Win ) (przez [GitHub]: mhammond / pywin32 - (master) pywin32 / win32 / src / win32file.i )

  • _get_dir_content (od punktu 1. ) może być zaimplementowany przy użyciu dowolnego z tych podejść (niektóre będą wymagały więcej pracy, a inne mniej)

    • Można wykonać zaawansowane filtrowanie (zamiast tylko pliku vs. katalog ): np. Argument include_folders można zastąpić innym (np. Filter_func ), który byłby funkcją, która przyjmuje ścieżkę jako argument: filter_func=lambda x: True(nie usuwa się cokolwiek) i wewnątrz _get_dir_content coś takiego: if not filter_func(entry_with_path): continue(jeśli funkcja nie powiedzie się dla jednego wpisu, zostanie pominięta), ale im bardziej skomplikowany staje się kod, tym dłużej trwa wykonanie
  • Nota bene! Ponieważ używana jest rekurencja, muszę wspomnieć, że wykonałem kilka testów na moim laptopie ( Win 10 x64 ), całkowicie niezwiązanych z tym problemem, a kiedy poziom rekurencji osiągał wartości gdzieś w zakresie (990 .. 1000) ( recursionlimit - 1000 (domyślnie)), mam StackOverflow :). Jeśli drzewo katalogów przekroczy ten limit (nie jestem ekspertem FS , więc nie wiem, czy to w ogóle możliwe), może to stanowić problem.
    Muszę również wspomnieć, że nie próbowałem zwiększać ograniczenia rekursji, ponieważ nie mam doświadczenia w tej dziedzinie (o ile mogę go zwiększyć, zanim będę musiał zwiększyć stos w OS) poziomu ograniczenia ), ale teoretycznie zawsze będzie możliwość niepowodzenia, jeśli głębokość katalogu jest większa niż najwyższa możliwa rekursja ograniczenia (na tej maszynie)

  • Próbki kodu służą wyłącznie do celów demonstracyjnych. Oznacza to, że nie wziąłem pod uwagę obsługi błędów (nie sądzę, aby istniała jakakolwiek próba / oprócz / else / wreszcie blok), więc kod nie jest niezawodny (powodem jest to, aby był tak prosty i krótki, jak to możliwe) ). W przypadku produkcji należy również dodać obsługę błędów

Inne podejścia:

  1. Używaj Pythona tylko jako opakowania

    • Wszystko odbywa się przy użyciu innej technologii
    • Ta technologia jest wywoływana z Pythona
    • Najsłynniejszym znanym mi smakiem jest to, co nazywam podejściem administratora systemu :

      • Użyj Pythona (lub dowolnego innego języka programowania w tym zakresie), aby wykonywać polecenia powłoki (i analizować ich wyniki)
      • Niektórzy uważają to za porządny hack
      • Uważam to bardziej za kiepskie obejście ( gainarie ), ponieważ akcja per se jest wykonywana z powłoki ( w tym przypadku cmd ), a zatem nie ma nic wspólnego z Pythonem .
      • Filtrowanie ( grep/ findstr) lub formatowanie wyjściowe można wykonać po obu stronach, ale nie zamierzam tego nalegać. Również celowo użyłem os.systemzamiast subprocess.Popen.
      (py35x64_test) E:\Work\Dev\StackOverflow\q003207219>"e:\Work\Dev\VEnvs\py35x64_test\Scripts\python.exe" -c "import os;os.system(\"dir /b root_dir\")"
      dir0
      dir1
      dir2
      dir3
      file0
      file1

    Zasadniczo należy unikać tego podejścia, ponieważ jeśli niektóre formaty wyjściowe poleceń nieco różnią się między wersjami / smakami systemu operacyjnego , należy również dostosować kod analizujący; nie wspominając o różnicach między lokalizacjami).

CristiFati
źródło
48

Naprawdę podobała mi się odpowiedź Adama , sugerująca użycie glob(), z modułu o tej samej nazwie. Pozwala to na dopasowanie wzorca do *s.

Ale jak zauważyli inni w komentarzach, glob()mogą się potknąć niespójnymi kierunkami cięcia. Aby temu zaradzić, sugeruję użycie funkcji join()i expanduser()w os.pathmodule, a być może także getcwd()funkcji w osmodule.

Jako przykłady:

from glob import glob

# Return everything under C:\Users\admin that contains a folder called wlp.
glob('C:\Users\admin\*\wlp')

Powyższe jest okropne - ścieżka została zakodowana na stałe i zawsze będzie działała w systemie Windows między nazwą dysku a \literą „s” zakodowaną na ścieżce.

from glob    import glob
from os.path import join

# Return everything under Users, admin, that contains a folder called wlp.
glob(join('Users', 'admin', '*', 'wlp'))

Powyższe działa lepiej, ale opiera się na nazwie folderu, Usersktóra często znajduje się w systemie Windows, a nie tak często w innych systemach operacyjnych. Powołuje się on także na użytkownika posiadającego specyficzną nazwę, admin.

from glob    import glob
from os.path import expanduser, join

# Return everything under the user directory that contains a folder called wlp.
glob(join(expanduser('~'), '*', 'wlp'))

Działa to doskonale na wszystkich platformach.

Kolejny świetny przykład, który działa idealnie na różnych platformach i robi coś nieco innego:

from glob    import glob
from os      import getcwd
from os.path import join

# Return everything under the current directory that contains a folder called wlp.
glob(join(getcwd(), '*', 'wlp'))

Mam nadzieję, że te przykłady pomogą ci zobaczyć moc kilku funkcji, które można znaleźć w standardowych modułach bibliotecznych Python.

ArtOfWarfare
źródło
4
Dodatkowa globalna zabawa: począwszy od Pythona 3.5, **działa tak długo, jak ustawisz recursive = True. Zobacz dokumenty tutaj: docs.python.org/3.5/library/glob.html#glob.glob
ArtOfWarfare
35
def list_files(path):
    # returns a list of names (with extension, without full path) of all files 
    # in folder path
    files = []
    for name in os.listdir(path):
        if os.path.isfile(os.path.join(path, name)):
            files.append(name)
    return files 
Apogentus
źródło
23

Jeśli szukasz implementacji find w Pythonie , jest to przepis, którego używam dość często:

from findtools.find_files import (find_files, Match)

# Recursively find all *.sh files in **/usr/bin**
sh_files_pattern = Match(filetype='f', name='*.sh')
found_files = find_files(path='/usr/bin', match=sh_files_pattern)

for found_file in found_files:
    print found_file

Zrobiłem z niego pakiet PyPI i istnieje również repozytorium GitHub . Mam nadzieję, że ktoś uzna to za potencjalnie przydatne dla tego kodu.

Yauhen Yakimovich
źródło
14

Aby uzyskać lepsze wyniki, możesz użyć listdir()metody osmodułu razem z generatorem (generator to potężny iterator, który utrzymuje swój stan, pamiętasz?). Poniższy kod działa poprawnie w obu wersjach: Python 2 i Python 3.

Oto kod:

import os

def files(path):  
    for file in os.listdir(path):
        if os.path.isfile(os.path.join(path, file)):
            yield file

for file in files("."):  
    print (file)

listdir()Metoda zwraca listę wpisów dla danego katalogu. Metoda os.path.isfile()zwraca, Truejeśli podany wpis jest plikiem. A yieldoperator rezygnuje FUNC ale zachowuje swój aktualny stan, i zwraca tylko nazwę wejściu wykryte jako plik. Wszystkie powyższe pozwalają nam zapętlić funkcję generatora.

Andy
źródło
11

Zwracanie listy bezwzględnych ścieżek plików, nie powraca do podkatalogów

L = [os.path.join(os.getcwd(),f) for f in os.listdir('.') if os.path.isfile(os.path.join(os.getcwd(),f))]
The2ndSon
źródło
2
Uwaga: os.path.abspath(f)byłby nieco tańszym zamiennikiem os.path.join(os.getcwd(),f).
ShadowRanger
Byłbym bardziej wydajny, gdybyś zaczął cwd = os.path.abspath('.'), a następnie używał cwdzamiast '.'i os.getcwd()przez cały czas, aby uniknąć mnóstwa zbędnych wywołań systemowych.
Martijn Pieters
10
import os
import os.path


def get_files(target_dir):
    item_list = os.listdir(target_dir)

    file_list = list()
    for item in item_list:
        item_dir = os.path.join(target_dir,item)
        if os.path.isdir(item_dir):
            file_list += get_files(item_dir)
        else:
            file_list.append(item_dir)
    return file_list

Tutaj używam struktury rekurencyjnej.

pah8J
źródło
To samo można osiągnąć tylko w jednej linii z pathlib:filter(Path.is_file, Path().rglob('*'))
Georgy
9

Mądry nauczyciel powiedział mi kiedyś, że:

Gdy istnieje kilka ustalonych sposobów na zrobienie czegoś, żaden z nich nie jest dobry dla wszystkich przypadków.

W ten sposób dodam rozwiązanie dla podzbioru problemu: dość często chcemy tylko sprawdzić, czy plik pasuje do łańcucha początkowego i łańcucha końcowego, bez wchodzenia do podkatalogów. Dlatego chcielibyśmy funkcji, która zwraca listę nazw plików, na przykład:

filenames = dir_filter('foo/baz', radical='radical', extension='.txt')

Jeśli chcesz najpierw zadeklarować dwie funkcje, możesz to zrobić:

def file_filter(filename, radical='', extension=''):
    "Check if a filename matches a radical and extension"
    if not filename:
        return False
    filename = filename.strip()
    return(filename.startswith(radical) and filename.endswith(extension))

def dir_filter(dirname='', radical='', extension=''):
    "Filter filenames in directory according to radical and extension"
    if not dirname:
        dirname = '.'
    return [filename for filename in os.listdir(dirname)
                if file_filter(filename, radical, extension)]

To rozwiązanie można łatwo uogólnić za pomocą wyrażeń regularnych (i możesz chcieć dodać patternargument, jeśli nie chcesz, aby twoje wzorce zawsze trzymały się początku lub końca nazwy pliku).

fralau
źródło
6

Korzystanie z generatorów

import os
def get_files(search_path):
     for (dirpath, _, filenames) in os.walk(search_path):
         for filename in filenames:
             yield os.path.join(dirpath, filename)
list_files = get_files('.')
for filename in list_files:
    print(filename)
shantanoo
źródło
4

Innym bardzo czytelnym wariantem dla Python 3.4+ jest pathlib.Path.glob:

from pathlib import Path
folder = '/foo'
[f for f in Path(folder).glob('*') if f.is_file()]

Łatwiej jest sprecyzować, np. Szukać tylko plików źródłowych Pythona, które nie są dowiązaniami symbolicznymi, również we wszystkich podkatalogach:

[f for f in Path(folder).glob('**/*.py') if not f.is_symlink()]
fhchl
źródło
3

Oto moja funkcja ogólnego przeznaczenia do tego. Zwraca listę ścieżek do plików zamiast nazw plików, ponieważ uważam, że jest to bardziej przydatne. Ma kilka opcjonalnych argumentów, dzięki którym jest wszechstronny. Na przykład często używam go z argumentami takimi jak pattern='*.txt'lub subfolders=True.

import os
import fnmatch

def list_paths(folder='.', pattern='*', case_sensitive=False, subfolders=False):
    """Return a list of the file paths matching the pattern in the specified 
    folder, optionally including files inside subfolders.
    """
    match = fnmatch.fnmatchcase if case_sensitive else fnmatch.fnmatch
    walked = os.walk(folder) if subfolders else [next(os.walk(folder))]
    return [os.path.join(root, f)
            for root, dirnames, filenames in walked
            for f in filenames if match(f, pattern)]
MarredCheese
źródło
2

Podam przykładową linijkę, w której jako źródło można podać sourcepath i typ pliku. Kod zwraca listę nazw plików z rozszerzeniem csv. Zastosowanie . na wypadek, gdyby wszystkie pliki musiały zostać zwrócone. Spowoduje to również rekurencyjne skanowanie podkatalogów.

[y for x in os.walk(sourcePath) for y in glob(os.path.join(x[0], '*.csv'))]

W razie potrzeby zmodyfikuj rozszerzenia plików i ścieżkę źródłową.

Vinodh Krishnaraju
źródło
1
Jeśli zamierzasz użyć glob, po prostu użyj glob('**/*.csv', recursive=True). Nie ma potrzeby łączenia tego z os.walk()rekurencją ( recursivei **są obsługiwane od wersji Python 3.5).
Martijn Pieters
2

Dla python2: pip zainstaluj rglob

import rglob
file_list=rglob.rglob("/home/base/dir/", "*")
print file_list
chris-piekarski
źródło
2

dircache jest „Przestarzałe od wersji 2.6: Moduł dircache został usunięty w Pythonie 3.0”.

import dircache
list = dircache.listdir(pathname)
i = 0
check = len(list[0])
temp = []
count = len(list)
while count != 0:
  if len(list[i]) != check:
     temp.append(list[i-1])
     check = len(list[i])
  else:
    i = i + 1
    count = count - 1

print temp
shaji
źródło
17
dirchache jest „Przestarzałe od wersji 2.6: Moduł dircache został usunięty w Pythonie 3.0”.
Daniel Reis,