Artık klasörlere ve dosyalara ayrı ayrı ulaşabiliyoruz. Peki belirli bir dosyayı ya da klasörü arıyorsak? Bu durumda dosya ve klasör adları arasında aradığımız karakterlere uygun sonuçları filtreleyecek yöntemlere ihtiyacımız var demektir.

Öncelikle bu konuyu işlerken kullanacağımız klasör ve dosya yapısını oluşturalım.

Çalışma klasörümüz altındaki dizin ve dosyaları aşağıdaki şekilde oluşturalım:
├── main.py
├── demetler/
|   ├── bilgi.txt
|   └── demet1.py
|   ├── demet2.py
|   └── veri.json
|
├── demet.py
├── ornek1.py
├── ornek2.py
├── veri1.json
├── veri2.json

 

Dosya/klasör yapısı işletim sisitemine ait olduğu için arama işlemlerinde de kullanacağımız temel modül os modülüdür. İlk işimiz en başta onu kod dosyamıza import etmek olmalıdır.

import os

 

Dosya/klasör isimleri de karakterlerden oluşan metinler olduğuna göre startswith(), endswith() gibi metin(string) metodlarını kullanabiliriz. Örneğin endswith() metodunu kullanarak belirli bir uzantıya sahip dosyaları bulabiliriz. zip, pdf, jpg, txt gibi belirli türdeki dosyaları bulmak için idealdir;

print("Klasördeki py uzantılı dosyalar; ")
with os.scandir(bulundugum_yer) as tarama:
    for belge in tarama:
        if belge.name.endswith("py"):
            print(belge.name)

Çıktı:

Klasördeki py uzantılı dosyalar; 
ornek1.py
ornek2.py
main.py
demet.py

 

Ya da aradığımız dosya veya klasörün adını biliyorsak startswith() metodunu kullanabiliriz;

print("Klasör içinde adı veri ile başlayan dosyalar; ")
with os.scandir(bulundugum_yer) as tarama:
    for belge in tarama:
        if belge.name.startswith("veri"):
            print(belge.name)

Çıktı:

Klasör içinde adı veri ile başlayan dosyalar; 
veri1.json
veri2.json

 

fnmatch Modülü

Ancak metin metodlarının da bazı kısıtlı kaldığı durumlar olabilir. Bu gibi durumlarda belirli bir duruma uygun dosyaları bulmak için fnmatch modülünü kullanabiliriz.  fnmatch modülünün dosya/klasör ararken bize yardımcı olabilecek pek çok fonksiyonu ve metodu vardır. Örneğin aynı adı taşıyan fnmatch() fonksiyonu dosya/klasör isimlerinde belirli kalıplara uyanları bulmak için * ve ? sembollerini kullanmamıza izin verir. Örneğin klasör içindeki tüm python dosyalarını bulmak isterseniz;

import os
import fnmatch

print("Klasördeki zip uzantılı sıkıştırılmış dosyalar; ")
with os.scandir(bulundugum_yer) as tarama:
    for belge in tarama:
        if fnmatch.fnmatch(belge.name, "*.py"):
            print(belge.name)

Çıktı:

Klasördeki py uzantılı dosyalar; 
ornek1.py
ornek2.py
demet.py
main.py

 

Ya da klasör içindeki “veri” kelimesiyle ile başlayan klasör ve dosyaları bulmak isterseniz;

import os
import fnmatch

print("Klasör içinde adı veri ile başlayan dosyalar ve klasörler; ")
with os.scandir(bulundugum_yer) as tarama:
    for belge in tarama:
        if fnmatch.fnmatch(belge.name, "veri*"):
            print(belge.name)

Çıktı:

Klasör içinde adı veri ile başlayan dosyalar ve klasörler; 
veri1.json
veri2.json

 

Çalışma klasörümüz içinde d harfi ile başlayan hem klasör hem de dosyalar var. Daha önceki konuda dosya ile klasör ayrımını is_file() ve is_dir() metodları ile yapabileceğimizi öğrenmiştik. Bu bilgiyi dosya/klasör aramak için kullandığımız fnmatch() fonksiyonu ile birlikte kullanırsak d ile başlayan dosya/klasör arama işlemini sadece dosyaları ya da sadece klasörleri bulacak şekilde özelleştirebiliriz;

import os
import fnmatch

print("Klasör içinde adı d ile başlayan dosyaları bulalım; ")
with os.scandir(bulundugum_yer) as tarama:
    for belge in tarama:
        if belge.is_file() and fnmatch.fnmatch(belge.name, "d*"):
            print(belge.name)

Çıktı:

Klasör içinde adı d ile başlayan dosyaları bulalım; 
demet.py

 

Tabii isviçre çakısı gibi tüm arama işlemlerimizde yardımcı olan fnmatch() fonksiyonunda * sembolünü sadece arama kelimesinin başında ve sonunda kullanmamıza gerek yok. Duruma göre kelime içinde de kullanabiliriz. Örneğin çalışma klasörü içindeki örnek dosyalarına ulaşmak istersek ;

import os
import fnmatch

print("Çalışma klasöründe yer alan Python örnek dosyalarımız: ")
with os.scandir(bulundugum_yer) as tarama:
  for belge in tarama:
    if fnmatch.fnmatch(belge.name, "ornek*.py"):
      print(belge.name)

Çıktı:

Çalışma klasöründe yer alan Python örnek dosyalarımız: 
ornek1.py
ornek2.py

Tabii, linux shell benzeri yazım stili de kullanarak çok spesifik aramalar da yapabilirsiniz;

import os
import fnmatch

print("İçinde sayısal ifadeler geçen dosya isimlerini bulalım;")
with os.scandir(bulundugum_yer) as tarama:
  for belge in tarama:
    if fnmatch.fnmatch(belge.name, "*[0-9]*"):
      print(belge.name)

Çıktı:

İçinde sayısal ifadeler geçen dosya isimlerini bulalım;
veri1.json
veri2.json
ornek1.py
ornek2.py

 

glob Modülü

Belirli bir şablona uyan klasör ve dosya arama işlemlerinde kullanabileceğimiz bir başka önemli modülümüz de glob modülüdür.

glob modülü içinde aynı ismi taşıyan glob() fonksiyonu tıpkı fnmatch modülü içinde aynı ismi taşıyan fnmatch() fonksiyonu gibi davranır. Ancak farklı yetenekleri de vardır tabii. Ama önce benzer yeteneklerine göz atalım. Bunun için de, fnmatch ile yapabildiğimiz yukarıdaki tüm örnekleri bir kez de glob modülünün glob() fonksiyonuyla yapalım ;

import glob

print("Klasördeki py uzantılı dosyalar; ")
print(glob.glob("*.py"))
for belge in glob.glob("*.py"):
  print(belge)
print("----------------------")
print("Klasör içinde adı veri ile başlayan dosyalar ve klasörler; ")
print(glob.glob("veri*"))
print("----------------------")
print("Klasör içinde adı d ile başlayan dizinleri bulalım; ")
tum=glob.glob("d*")
dosyalar=glob.glob("d*.*")
for eleman in tum:
  if eleman not in dosyalar:
    print(eleman)
print("----------------------")
print("Çalışma klasöründe yer alan Python örnek dosyalarımız: ")
print(glob.glob("ornek*.py"))
print("----------------------")
print("İçinde sayısal ifadeler geçen dosya isimlerini bulalım;")
print(glob.glob("*[0-9]*"))
print("----------------------")
for belge in glob.iglob("**/*.py", recursive=True):
  print(belge)

Çıktı:

Klasördeki py uzantılı dosyalar; 
['ornek1.py', 'ornek2.py', 'demet.py', 'main.py']
ornek1.py
ornek2.py
demet.py
main.py
----------------------
Klasör içinde adı veri ile başlayan dosyalar ve klasörler; 
['veri1.json', 'veri2.json']
----------------------
Klasör içinde adı d ile başlayan dizinleri bulalım; 
demetler
----------------------
Çalışma klasöründe yer alan Python örnek dosyalarımız: 
['ornek1.py', 'ornek2.py']
----------------------
İçinde sayısal ifadeler geçen dosya isimlerini bulalım: 
['veri1.json', 'veri2.json', 'ornek1.py', 'ornek2.py']

 

Burada glob ile fnmatch arasındaki önemli bir farklılık dikkatinizi çekmiş olmalı; fnmatch sonucu bize nesne olarak döndürürken, glob bir liste olarak döndürmektedir. İsterseniz dönen sonuç üzerinde listeler ile ilgili metodları uygulayabilirsiniz. Biz ilk örnekte bir döngü ile liste içinde depolanan verileri tek tek ekrana yazdırdık.

Dikkat ettiyseniz, klasör içinde adı d ile başlayan klasörleri bulalım örneğinde is_file() ve is_dir() fonksiyonlarını kullanamadık. Çünkü glob’da bu foksiyonlar yoktur. Bu nedenle daha önce öğrendiğimiz listeler ile ilgili bilgilerimizi kullanarak sonuca ulaştık. Ayrıca biliyoruz ki, dosyalar nokta ile başlayan ve dosya türünü belirleyen bir uzantıya sahip olurlar. Biz de örnekte glob ile tüm sonuçları liste olarak aldık ve yine glob ile dosya ismi şablonuna uyan bir liste daha oluşturup, dosyalar listesinin elemanlarını tüm sonuçlardan çıkartarak sonuçlar içindeki klasörlerin isimlerine ulaştık. Yani klasör isimlerini bulabilmek için hazır bir metod olmadığından kendi yolumuzu kodladık.

Şimdi gelelim glob() fonksiyonunun fnmatch() fonksiyonundan farklı özelliklerine…

Her ne kadar fnmatch ile iç içe döngüler kurarak bir çözüm yolu bulunabilir olsa da, glob’un bize sunduğu daha kısa yöntem ile hiyerarşik olarak alt klasörler içinde de arama yapmanız mümkün hale gelir. Bunun için glob modülünün iglob() fonksiyonunu ikinci parametre olarak recursive=true vererek kullanırız;

for belge in glob.iglob("**/*.py", recursive=True):
  print(belge)

Çıktı:

ornek1.py
ornek2.py
demet.py
main.py
demetler/demet1.py
demetler/demet2.py

Yukarıda ele aldığımız örnek mevcut çalışma klasörümüz ve onun alt klasöründeki py uzantılı dosyaları bulup ekrana yazdırıyor.

 

walk() Modülü ile Klasörlerde Dolaşmak

Bir programcı olarak dosyalar ve klasörler ile ilgili işlemleri gerçekleştirirken en sık ihtiyaç duyacağımız işlerden biri de klasörler arasında dolaşmak olacaktır. os modülünde yer alan walk() fonksiyonu bunun için gerekli yolları bize sunar.

walk()  fonksiyonu, klasör yapısı içinde hiyerarşik olarak yukarıdan aşağı ya da aşağıdan yukarı doğru gezinerek dosya isimlerini bize getiren bir fonksiyondur.

Şimdi bu çalışmamızda örneklerimiz için kullanmak üzere önceki klasör yapımıza yeni bir alt klasör daha ekleyelim ve içine de benzer dosyalar oluşturalım. Klasör yapımız şu şekilde olacaktır;

├── main.py
├── demetler/
|   ├── bilgi.txt
|   └── demet1.py
|   ├── demet2.py
|   └── veri.json
├── listeler/
|   ├── bilgi.txt
|   └── liste1.py
|   ├── liste2.py
|   ├── liste3.py
|   ├── veri1.json
|   └── veri2.json
|
├── demet.py
├── ornek1.py
├── ornek2.py
├── veri1.json
├── veri2.json

Şimdi çalışma klasörümüz içinde yer alan tüm klasör ve dosyaların bir listesini oluşturalım;

for x in os.walk(bulundugum_yer):
  print(x)

Çıktı:

('/home/runner/dosya-dizin-arama', ['demetler', 'listeler'], ['veri1.json', 'veri2.json', 'ornek1.py', 'ornek2.py', 'demet.py', 'main.py'])
('/home/runner/dosya-dizin-arama/demetler', [], ['demet1.py', 'demet2.py', 'bilgi.txt', 'veri.json'])
('/home/runner/dosya-dizin-arama/listeler', [], ['veri1.json', 'veri2.json', 'bilgi.txt', 'liste1.py', 'liste2.py', 'liste3.py'])

Kod çalıştığında bize verdiği çıktıyı inceleyecek olursak, şu şekilde bir yapıya sahip olduğunu fark ederiz; Bir veri koleksiyonu olan demet içinde önce klasör yolu, sonra kalsördeki alt klasörlerin bir listesi, sonrada klasör içindeki dosyaların bir listesi şeklinde karşımıza çıkıyor. Sonra alt klasörleri sırayla ele alıp aynı yapıyı onlar üzerinde tekrar ederek yazdırıyor.

Yani os modülünün walk() fonksiyonu bize 3 veri sağlıyor; dosya yolu, alt klasörler listesi, dosya listesi. Bu verileri birer değişkene atamak için;

for anaDizin, altDizinler, dosyalar in os.walk(bulundugum_yer):
ifadesini kullanabiliriz.

Daha sonra hangi değişkenin değerini yazdırmak istersek döngü içinde onu kullanırız.

for anaDizin, altDizinler, dosyalar in os.walk(bulundugum_yer):
  print("Klasör: ",anaDizin)
  print("Alt Dizinleri: ", altDizinler)
  print("İçindeki Dosyalar: ", dosyalar)
  print("----------------------")

Çıktı:

Klasör: /home/runner/dosya-dizin-arama
Alt Dizinleri: ['demetler', 'listeler']
İçindeki Dosyalar: ['veri1.json', 'veri2.json', 'ornek1.py', 'ornek2.py', 'demet.py', 'main.py']
----------------------
Klasör: /home/runner/dosya-dizin-arama/demetler
Alt Dizinleri: []
İçindeki Dosyalar: ['demet1.py', 'demet2.py', 'bilgi.txt', 'veri.json']
----------------------
Klasör: /home/runner/dosya-dizin-arama/listeler
Alt Dizinleri: []
İçindeki Dosyalar: ['veri1.json', 'veri2.json', 'bilgi.txt', 'liste1.py', 'liste2.py', 'liste3.py']
----------------------

 

Gördüğünüz gibi anaDizin değişkeni klasör ve alt klasörlerin yollarını verdiğine göre her bir dosyanın direkt erişim sağlayan dosya yoluna şu şekilde ulaşabiliriz;

for anaDizin, altDizinler, dosyalar in os.walk(bulundugum_yer):
  for dosya in dosyalar:
    print(anaDizin+"/"+ dosya)

Çıktı:

/home/runner/dosya-dizin-arama/veri1.json
/home/runner/dosya-dizin-arama/veri2.json
/home/runner/dosya-dizin-arama/ornek1.py
/home/runner/dosya-dizin-arama/ornek2.py
/home/runner/dosya-dizin-arama/demet.py
/home/runner/dosya-dizin-arama/main.py
/home/runner/dosya-dizin-arama/demetler/demet1.py
/home/runner/dosya-dizin-arama/demetler/demet2.py
/home/runner/dosya-dizin-arama/demetler/bilgi.txt
/home/runner/dosya-dizin-arama/demetler/veri.json
/home/runner/dosya-dizin-arama/.upm/store.json
/home/runner/dosya-dizin-arama/listeler/veri1.json
/home/runner/dosya-dizin-arama/listeler/veri2.json
/home/runner/dosya-dizin-arama/listeler/bilgi.txt
/home/runner/dosya-dizin-arama/listeler/liste1.py
/home/runner/dosya-dizin-arama/listeler/liste2.py
/home/runner/dosya-dizin-arama/listeler/liste3.py

 

Burada yer alan yapı özellikle web uygulamalarında resim ya da diğer dosya bağlantıları verirken, masaüstü uygulamalarda ise dosya kopyalama ve taşıma yaparken çok kullanışlı olacaktır.

Her öğrendiğimiz konuyu daha önce öğrendiklerimiz ile birleştirebilmek ve birlikte kullanmayı denemek de çok önemlidir. Örneğin daha bu konuda öğrendiğimiz dosya buma işlemini alt dizin içeriklerine ulaşmamızı sağlayan walk() fonksiyonuyla beraber kullanarak çalışma klasörümüz altında ve alt klasörlerinde bulunan tüm python dosyalarını bulalım;

for anaDizin, altDizinler, dosyalar in os.walk(bulundugum_yer):
  for dosya in dosyalar:
    if dosya.endswith(".py"):
      print(anaDizin+"/"+ dosya)

Çıktı:

/home/runner/dosya-dizin-arama/ornek1.py
/home/runner/dosya-dizin-arama/ornek2.py
/home/runner/dosya-dizin-arama/demet.py
/home/runner/dosya-dizin-arama/main.py
/home/runner/dosya-dizin-arama/demetler/demet1.py
/home/runner/dosya-dizin-arama/demetler/demet2.py
/home/runner/dosya-dizin-arama/listeler/liste1.py
/home/runner/dosya-dizin-arama/listeler/liste2.py
/home/runner/dosya-dizin-arama/listeler/liste3.py

 

Gördüğünüz gibi soruyu yazmak bile kodu yazmaktan uzun sürdü. Yani az kodla çok iş yapmak konusunda Python ve modülleri çok faydalıdır.

 

Bu konuda işlenen örneklerin kodlarını da şurada görebilirsiniz;

https://repl.it/@ObenSEVEN/dosya-dizin-arama