Bir dizindeki dosyaların filtrelenmiş bir listesini alma


281

Python kullanarak bir dizindeki dosyaların listesini almaya çalışıyorum, ama TÜM dosyaların bir listesini istemiyorum.

Aslında istediğim şu gibi bir şey yapmak ama Python kullanarak ve ls yürütme yeteneğidir.

ls 145592*.jpg

Bunun için yerleşik bir yöntem yoksa, şu anda bir sonuçları için yineleme os.listdir()ve tüm eşleşen dosyaları yeni bir listeye eklemek için bir for döngüsü yazmayı düşünüyorum .

Ancak, bu dizinde çok fazla dosya var ve bu nedenle daha verimli bir yöntem (veya yerleşik bir yöntem) olduğunu umuyorum.


[Bu bağlantı size yardımcı olabilir :) Bir dizindeki dosyaların filtrelenmiş bir listesini alın] ( codereview.stackexchange.com/a/33642 )
sha111

Uygulamanız için önemliyse, sıralama düzenine özellikle dikkat edebileceğinizi unutmayın.
20'de lumbric

Yanıtlar:


385

21
Oh, Python belgelerinin glob () "aslında bir subshell çağırmadan değil, os.listdir () ve fnmatch.fnmatch () işlevlerini kullanarak yapıldığını söylediğini fark ettim". Başka bir deyişle, glob (), birinin beklediği verimlilik iyileştirmelerine sahip değildir.
Ben Hoyt

5
Temel bir fark vardır: glob.glob('145592*.jpg')Dosyaların tüm mutlak yolunu ls 145592*.jpgyazdırırken yalnızca dosya listesini yazdırır.
Ébe Isaac

8
@Ben Neden bir alt kabuğun (alt işlem) çağrılmasında verimlilik artışı olur?
Paulo Neves

7
@PauloNeves: Doğru, yukarıdaki yorumum 7 yıl sonra bana da mantıklı gelmiyor. :-) Ben glob()joker filtreleme yapmak için özel işletim sistemi çağrıları yerine, sadece listdir + fnmatch kullanır aslında bahsettiğini tahmin ediyorum . Örneğin, Windows'ta FindFirstFileAPI, işletim sisteminin filtrelemeyi doğrudan ve muhtemelen daha verimli bir şekilde yapması için joker karakterler belirtmenize izin verir (Linux'ta bir eşdeğer olduğunu düşünmüyorum).
Ben Hoyt

1
@marsh: Her zaman olduğu gibi, sürecin geçerli çalışma dizini.
Ignacio Vazquez-Abrams

125

glob.glob()kesinlikle bunu yapmanın yolu (Ignacio'ya göre). Ancak, daha karmaşık bir eşleştirmeye ihtiyacınız varsa, bunu bir liste kavrayışı ve re.match()benzeri bir şeyle yapabilirsiniz:

files = [f for f in os.listdir('.') if re.match(r'[0-9]+.*\.jpg', f)]

Daha esnek, ancak belirttiğiniz gibi, daha az verimli.


Bu kesinlikle daha güçlü görünüyor. Örneğin, böyle bir şey yapmak zorunda[0-9]+
demongolem

3
Evet, kesinlikle daha güçlü - ancak fnmatch [0123456789]dizileri destekliyor ( bkz. Dokümanlar ) ve ayrıca fnmatch.filter()bu döngüyü biraz daha verimli hale getiren bir işleve sahiptir.
Ben Hoyt

49

Basit tutun:

import os
relevant_path = "[path to folder]"
included_extensions = ['jpg','jpeg', 'bmp', 'png', 'gif']
file_names = [fn for fn in os.listdir(relevant_path)
              if any(fn.endswith(ext) for ext in included_extensions)]

Bu liste anlama biçimini tercih ediyorum çünkü İngilizce iyi okuyor.

Dördüncü satırı şu şekilde okudum: Yolum için os.listdir dosyasındaki her fn için, yalnızca dahil edilen uzantılarımdan herhangi biriyle eşleşenleri verin.

Acemi python programcılarının filtreleme için liste kavrayışlarını kullanmaya alışması zor olabilir ve çok büyük veri kümeleri için bellek yükü olabilir, ancak bir dizin ve diğer basit dize filtreleme görevlerini listelemek için liste kavramaları daha temiz olur belgelendirilebilir kod.

Bu tasarımla ilgili tek şey, bir liste yerine bir dize geçirme hatasına karşı sizi korumamasıdır. Örneğin, bir dizeyi yanlışlıkla bir listeye dönüştürür ve bir dizenin tüm karakterlerini kontrol ederseniz, sonuçta bir dizi yanlış pozitif alabilirsiniz.

Ancak, düzeltilmesi kolay bir sorunun olması, anlaşılması zor bir çözümden daha iyidir.


5
any()Burada herhangi bir ihtiyaç olmadığı için değil, çünkü str.endswith()bir dizi son alır . if fn.endswith(included_extentensions)fazlasıyla yeterli.
Martijn Pieters

3
str.endswith(seq)Martijn'ın işaret etmediğinin verimsizliğinin yanı sıra , bu doğru değil, çünkü bir dosyanın .extbu uzantıya sahip olması için bitmesi gerekiyor. Bu kod ayrıca (örneğin) "myjpg" adlı bir dosyayı veya sadece "png" adlı bir dizini bulur. Düzeltmek için, her uzantıya included_extensionsa önekini eklemeniz yeterlidir ..
Ben Hoyt

Açıkçası çalıştırılmayan veya çalıştırılamayan cevaplarda her zaman koddan biraz dikkatliyim. Değişken included_extensionsvs included_extentsions? Yazık çünkü bu benim tercih ettiğim cevap.
falı


17

Modüllü filtre glob:

Dünyayı içe aktar

import glob

Joker Kartları:

files=glob.glob("data/*")
print(files)

Out:

['data/ks_10000_0', 'data/ks_1000_0', 'data/ks_100_0', 'data/ks_100_1',
'data/ks_100_2', 'data/ks_106_0', 'data/ks_19_0', 'data/ks_200_0', 'data/ks_200_1', 
'data/ks_300_0', 'data/ks_30_0', 'data/ks_400_0', 'data/ks_40_0', 'data/ks_45_0', 
'data/ks_4_0', 'data/ks_500_0', 'data/ks_50_0', 'data/ks_50_1', 'data/ks_60_0', 
'data/ks_82_0', 'data/ks_lecture_dp_1', 'data/ks_lecture_dp_2']

Fiter uzantısı .txt:

files = glob.glob("/home/ach/*/*.txt")

Tek bir karakter

glob.glob("/home/ach/file?.txt")

Sayı Aralıkları

glob.glob("/home/ach/*[0-9]*")

Alfabe Aralıkları

glob.glob("/home/ach/[a-c]*")

12

Ön kod

import glob
import fnmatch
import pathlib
import os

pattern = '*.py'
path = '.'

Çözüm 1 - "Glob" kullanın

# lookup in current dir
glob.glob(pattern)

In [2]: glob.glob(pattern)
Out[2]: ['wsgi.py', 'manage.py', 'tasks.py']

Çözüm 2 - "OS" + "fnmatch" kullanın

Varyant 2.1 - Şu anki yön arama

# lookup in current dir
fnmatch.filter(os.listdir(path), pattern)

In [3]: fnmatch.filter(os.listdir(path), pattern)
Out[3]: ['wsgi.py', 'manage.py', 'tasks.py']

Varyant 2.2 - Yinelemeli arama

# lookup recursive
for dirpath, dirnames, filenames in os.walk(path):

    if not filenames:
        continue

    pythonic_files = fnmatch.filter(filenames, pattern)
    if pythonic_files:
        for file in pythonic_files:
            print('{}/{}'.format(dirpath, file))

Sonuç

./wsgi.py
./manage.py
./tasks.py
./temp/temp.py
./apps/diaries/urls.py
./apps/diaries/signals.py
./apps/diaries/actions.py
./apps/diaries/querysets.py
./apps/library/tests/test_forms.py
./apps/library/migrations/0001_initial.py
./apps/polls/views.py
./apps/polls/formsets.py
./apps/polls/reports.py
./apps/polls/admin.py

Çözüm 3 - "Pathlib" Kullanın

# lookup in current dir
path_ = pathlib.Path('.')
tuple(path_.glob(pattern))

# lookup recursive
tuple(path_.rglob(pattern))

Notlar:

  1. Python 3.4 üzerinde test edildi
  2. "Pathlib" modülü sadece Python 3.4'e eklendi
  3. Python 3.5, glob.glob https://docs.python.org/3.5/library/glob.html#glob.glob ile özyinelemeli arama için bir özellik ekledi . Makinem Python 3.4 ile kurulduğundan, bunu test etmedim.

9

Dosyalarınızı özyinelemeli olarak listelemek için os.walk kullanın

import os
root = "/home"
pattern = "145992"
alist_filter = ['jpg','bmp','png','gif'] 
path=os.path.join(root,"mydir_to_scan")
for r,d,f in os.walk(path):
    for file in f:
        if file[-3:] in alist_filter and pattern in file:
            print os.path.join(root,file)

Dilim gerekmez; file.endswith(alist_filter)yeterlidir.
Martijn Pieters

5
import os

dir="/path/to/dir"
[x[0]+"/"+f for x in os.walk(dir) for f in x[2] if f.endswith(".jpg")]

Bu, tam yollarıyla jpg dosyalarının bir listesini verecektir. Yalnızca dosya adları için x[0]+"/"+file değiştirebilirsiniz f. İstediğiniz f.endswith(".jpg")dize koşuluyla da değiştirebilirsiniz .


3

ayrıca daha yüksek düzeyli bir yaklaşım da hoşunuza gidebilir (findtools olarak uyguladım ve paketledim ):

from findtools.find_files import (find_files, Match)


# Recursively find all *.txt files in **/home/**
txt_files_pattern = Match(filetype='f', name='*.txt')
found_files = find_files(path='/home', match=txt_files_pattern)

for found_file in found_files:
    print found_file

ile kurulabilir

pip install findtools

2

"Path / to / images" klasöründe "jpg" ve "png" uzantılı dosya adları:

import os
accepted_extensions = ["jpg", "png"]
filenames = [fn for fn in os.listdir("path/to/images") if fn.split(".")[-1] in accepted_extensions]


1

Python standart kütüphane 3.4 ve üzeri için mevcut olan pathlib'i kullanabilirsiniz .

from pathlib import Path

files = [f for f in Path.cwd().iterdir() if f.match("145592*.jpg")]

1

Deseni tanımlayabilir ve kontrol edebilirsiniz. Burada hem başlangıç ​​hem de bitiş desenini aldım ve onları dosya adında arıyorum. FILES, bir dizindeki tüm dosyaların listesini içerir.

import os
PATTERN_START = "145592"
PATTERN_END = ".jpg"
CURRENT_DIR = os.path.dirname(os.path.realpath(__file__))
for r,d,FILES in os.walk(CURRENT_DIR):
    for FILE in FILES:
        if PATTERN_START in FILE and PATTERN_END in FILE:
            print FILE

0

Str.split () nasıl olur? İçe aktarılacak bir şey yok.

import os

image_names = [f for f in os.listdir(path) if len(f.split('.jpg')) == 2]


Bu, @ ramsey0'in cevabını kullanarak benzer görünüyor f.endswith('.jpg')(ama aynı zamanda seçecek filename.jpg.ext)
anjsimmo

-1

Subprocess.check_ouput () öğesini şu şekilde kullanabilirsiniz:

import subprocess

list_files = subprocess.check_output("ls 145992*.jpg", shell=True) 

Tabii ki, tırnaklar arasındaki dize kabukta yürütmek ve çıktıyı saklamak istediğiniz herhangi bir şey olabilir.


Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.