รับรายการไฟล์ที่ถูกกรองในไดเรกทอรี


281

ฉันกำลังพยายามรับรายการไฟล์ในไดเรกทอรีโดยใช้ Python แต่ฉันไม่ต้องการรายการไฟล์ทั้งหมด

สิ่งที่ฉันต้องการเป็นหลักคือความสามารถในการทำสิ่งต่อไปนี้ แต่ใช้ Python และไม่เรียกใช้ ls

ls 145592*.jpg

หากไม่มีวิธีการในตัวสำหรับสิ่งนี้ฉันกำลังคิดที่จะเขียนลูปสำหรับวนซ้ำเพื่อทำซ้ำผลลัพธ์ของos.listdir()และเพื่อผนวกไฟล์ที่ตรงกันทั้งหมดลงในรายการใหม่

อย่างไรก็ตามมีไฟล์จำนวนมากในไดเรกทอรีนั้นและฉันหวังว่าจะมีวิธีที่มีประสิทธิภาพมากขึ้น (หรือวิธีการในตัว)


[ลิงค์นี้อาจช่วยคุณ :) รับรายการไฟล์ที่ถูกกรองในไดเรกทอรี] ( codereview.stackexchange.com/a/33642 )
sha111

โปรดทราบว่าคุณอาจใช้ความระมัดระวังเป็นพิเศษเกี่ยวกับการเรียงลำดับหากนี่เป็นสิ่งสำคัญสำหรับแอปพลิเคชันของคุณ
lumbric

คำตอบ:


385

21
โอ้ฉันเพิ่งสังเกตเห็นว่าเอกสาร Python บอกว่า glob () "ทำได้โดยการใช้ฟังก์ชั่น os.listdir () และ fnmatch.fnmatch () ในคอนเสิร์ตไม่ใช่การเรียก subshell จริง ๆ " กล่าวอีกนัยหนึ่ง glob () ไม่มีการปรับปรุงประสิทธิภาพอย่างใดอย่างหนึ่งที่อาจคาดหวัง
เบ็นฮอยต์

5
มีความแตกต่างหลักประการหนึ่ง: glob.glob('145592*.jpg')พิมพ์เส้นทางที่แน่นอนทั้งหมดของไฟล์ขณะls 145592*.jpgพิมพ์เฉพาะรายการไฟล์
Ébe Isaac

8
@Ben เหตุใดการเรียกใช้ subshell (subprocess) จึงมีการปรับปรุงประสิทธิภาพ
Paulo Neves

7
@PauloNeves: จริงความคิดเห็นของฉันข้างต้นไม่สมเหตุสมผลกับฉัน 7 ปีต่อมาเช่นกัน :-) ฉันเดาว่าฉันหมายถึงความจริงที่ว่าglob()แค่ใช้ listdir + fnmatch แทนที่จะเรียกระบบปฏิบัติการพิเศษเพื่อทำการกรองสัญลักษณ์แทน ตัวอย่างเช่นบน Windows FindFirstFileAPI ช่วยให้คุณระบุ wildcard ดังนั้นระบบปฏิบัติการจะทำการกรองโดยตรงและมีประสิทธิภาพมากขึ้น (ฉันไม่คิดว่ามันจะเทียบเท่ากับ Linux)
เบ็นฮอยต์

1
@marsh: เช่นเคยไดเรกทอรีการทำงานปัจจุบันของกระบวนการ
Ignacio Vazquez-Abrams

125

glob.glob()แน่นอนเป็นวิธีที่จะทำ (ตามอิกนาชิโอ) อย่างไรก็ตามหากคุณต้องการการจับคู่ที่ซับซ้อนมากขึ้นคุณสามารถทำมันด้วยรายการความเข้าใจและre.match()สิ่งที่ต้องการ:

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

มีความยืดหยุ่นมากขึ้น แต่เมื่อคุณทราบว่ามีประสิทธิภาพน้อยลง


ดูเหมือนว่าจะมีประสิทธิภาพมากกว่านี้แน่นอน ตัวอย่างเช่นต้องทำอะไรบางอย่าง[0-9]+
demongolem

3
ใช่มีประสิทธิภาพมากขึ้นแน่นอน - อย่างไรก็ตาม fnmatch รองรับ[0123456789]ลำดับ ( ดูเอกสาร ) และยังมีfnmatch.filter()ฟังก์ชันที่ทำให้ลูปนี้มีประสิทธิภาพมากขึ้นเล็กน้อย
เบ็นฮอยต์

49

ง่าย ๆ เข้าไว้:

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)]

ฉันชอบความเข้าใจในรายการรูปแบบนี้เพราะอ่านเป็นภาษาอังกฤษได้ดี

ฉันอ่านบรรทัดที่สี่เป็น: สำหรับแต่ละ fn ใน os.listdir สำหรับเส้นทางของฉันให้เฉพาะอันที่ตรงกับส่วนขยายใด ๆ ที่รวมอยู่

อาจเป็นเรื่องยากสำหรับโปรแกรมเมอร์ python สามเณรที่คุ้นเคยกับการใช้ list comprehensions สำหรับการกรองและอาจมีโอเวอร์เฮดหน่วยความจำสำหรับชุดข้อมูลขนาดใหญ่มาก แต่สำหรับรายการไดเรกทอรีและงานกรองสตริงแบบง่าย ๆ รายการ comprehensions นำไปสู่ความสะอาดมากขึ้น รหัสเอกสาร

สิ่งเดียวที่เกี่ยวกับการออกแบบนี้คือมันไม่ได้ป้องกันคุณจากการทำผิดพลาดของการส่งผ่านสายแทนที่จะเป็นรายการ ตัวอย่างเช่นหากคุณแปลงสตริงเป็นรายการโดยไม่ตั้งใจและสิ้นสุดการตรวจสอบกับอักขระทั้งหมดของสตริงคุณอาจได้รับผลบวกปลอม

แต่จะดีกว่าหากมีปัญหาที่แก้ไขได้ง่ายกว่าโซลูชันที่เข้าใจยาก


5
ไม่ว่ามีความต้องการany()ที่นี่เพราะstr.endswith()ใช้ลำดับของตอนจบ if fn.endswith(included_extentensions)เป็นมากกว่าเพียงพอ
Martijn Pieters

3
นอกเหนือจากความไร้ประสิทธิภาพของการไม่ใช้str.endswith(seq)Martijn ที่ชี้ให้เห็นแล้วสิ่งนี้ไม่ถูกต้องเพราะไฟล์จะต้องจบด้วย.extเพื่อให้มีนามสกุลนั้น รหัสนี้จะค้นหา (ตัวอย่าง) ไฟล์ชื่อ "myjpg" หรือไดเรกทอรีที่ชื่อ just "png" ในการแก้ไขปัญหาเพียงแค่คำนำหน้าแต่ละส่วนขยายในด้วยincluded_extensions .
Ben Hoyt

ฉันระมัดระวังโค้ดเล็กน้อยในคำตอบที่เห็นได้ชัดว่าไม่ได้ทำงานหรือไม่สามารถทำงานได้ ตัวแปรincluded_extensionsVS included_extentsions? น่าเสียดายเพราะนี่คือคำตอบที่ฉันต้องการ
ศุภนิมิต

39

ตัวเลือกอื่น:

>>> import os, fnmatch
>>> fnmatch.filter(os.listdir('.'), '*.py')
['manage.py']

https://docs.python.org/3/library/fnmatch.html


5
นี่คือสิ่งที่globเกิดขึ้นในบรรทัดเดียว
Itay Grudev

ความแตกต่างเพียงอย่างเดียวคือglobส่งคืนพา ธ เต็มเมื่อเทียบกับos.listdirเพียงแค่คืนชื่อไฟล์ อย่างน้อยนี่คือสิ่งที่เกิดขึ้นใน Python 2
Karthic Raghupathi

17

กรองด้วยglobโมดูล:

นำเข้าแบบกลม

import glob

ไวลด์การ์ด:

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 .txt:

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

ตัวละครเดียว

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

ช่วงจำนวน

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

ช่วงตัวอักษร

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

12

รหัสเบื้องต้น

import glob
import fnmatch
import pathlib
import os

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

โซลูชันที่ 1 - ใช้ "glob"

# lookup in current dir
glob.glob(pattern)

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

โซลูชันที่ 2 - ใช้ "os" + "fnmatch"

Variant 2.1 - ค้นหาใน dir ปัจจุบัน

# 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']

Variant 2.2 - การค้นหาแบบเรียกซ้ำ

# 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))

ผลลัพธ์

./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

โซลูชันที่ 3 - ใช้ "pathlib"

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

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

หมายเหตุ:

  1. ทดสอบกับ Python 3.4
  2. เพิ่มโมดูล "pathlib" ใน Python 3.4 เท่านั้น
  3. ธ 3.5 เพิ่มคุณสมบัติสำหรับการค้นหา recursive กับ glob.glob https://docs.python.org/3.5/library/glob.html#glob.glob เนื่องจากเครื่องของฉันติดตั้งด้วย Python 3.4 ฉันยังไม่ได้ทดสอบ

9

ใช้ os.walk เพื่อแสดงรายการไฟล์ซ้ำ

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)

ไม่จำเป็นต้องฝาน file.endswith(alist_filter)ก็เพียงพอแล้ว
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")]

นี่จะให้รายการของไฟล์ jpg พร้อมเส้นทางแบบเต็ม คุณสามารถแทนที่x[0]+"/"+fด้วยfเพียงแค่ชื่อไฟล์ คุณสามารถแทนที่f.endswith(".jpg")ด้วยเงื่อนไขสตริงใด ๆ ที่คุณต้องการ


3

คุณอาจชอบวิธีการระดับสูงมากขึ้น (ฉันได้ดำเนินการและบรรจุเป็นfindtools ):

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

สามารถติดตั้งได้ด้วย

pip install findtools



1

คุณสามารถกำหนดรูปแบบและตรวจสอบได้ ที่นี่ฉันได้นำรูปแบบการเริ่มต้นและสิ้นสุดและมองหาพวกเขาในชื่อไฟล์ ไฟล์มีรายการไฟล์ทั้งหมดในไดเรกทอรี

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 () ล่ะ? ไม่มีการนำเข้า

import os

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

2
นี่คล้ายกับคำตอบที่@gypsy
Sushanth

นี้ดูเหมือนจะคล้ายกับคำตอบของ @ ramsey0 ใช้f.endswith('.jpg')( แต่ยังจะเลือกfilename.jpg.ext)
anjsimmo

-1

คุณสามารถใช้ subprocess.check_ouput () เป็น

import subprocess

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

แน่นอนสตริงระหว่างอัญประกาศสามารถเป็นอะไรก็ได้ที่คุณต้องการเรียกใช้ในเชลล์และเก็บเอาท์พุท


ปัญหาเดียวเท่านั้น การส่งออกไม่ควรจะแยกวิเคราะห์ls
ivan_pozdeev
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.