singrium
singrium

Reputation: 3016

fnmatch does not display all filenames that match

I have a folder containing 5 files named respectively 'out1.jpg', 'out2a.jpg', 'out2b.jpg', 'out3.jpg' and 'out4.jpg' in addition to other files in different formats. I have this Python script which is supposed to print all the filenames that match:

import fnmatch
import os

c = 1
for file in os.listdir('.'):
    if fnmatch.fnmatch(file, 'out'+str(c)+'*.jpg'):
        print(file)
        c +=1

However, when I run this script,the output is limited to the following:

out1.jpg
out2a.jpg
out3.jpg

Anyone please has an idea how to change the script in order to display all the filenames that match (which are the 5 filenames that I mentioned)?

Upvotes: 2

Views: 1672

Answers (3)

bruno desthuilliers
bruno desthuilliers

Reputation: 77902

You are increasing c on each iteration (well, on each iteration that found a match but anyway...), so it cannot obviously match "out2a.jpg" AND "out2b.jpg". Assuming you want all file names that match "out" + some number + eventually something else, you can use character ranges instead; ie:

for file in os.listdir('.'):
    if fnmatch.fnmatch(file, 'out[0-9]*.jpg'):
        print(file)

NB : you might have to adjust the exact fnmatch pattern according to your needs and what you have in your directory.

You can also use glob.glob instead, which is both simpler and (according to the doc) more efficient:

import glob
for file in glob("out[0-9]*.jpg"):
    print(file)

EDIT :

I totally understand why it does not display out2a.jpg and out2b.jpg together, but I didn't get why out4.jpg is not displayed!

Quite simply because os.listdir() does not necessarily returns the filenames in the same order as you seemed to expect (on my linux station here, "out4.jpg" comes before the other "outXXX.jpg" files). You can inspect what's happening just by adding a couple prints:

c = 1
for file in os.listdir('.'):
    exp = 'out{}*.jpg'.format(c)
    print("file: {} - c : {} - exp : {}".format(file, c, exp))
    if fnmatch.fnmatch(file, exp):
        print(file)
        c +=1

And the result here:

file: sofnm.py~ - c : 1 - exp : out1*.jpg
file: out4.jpg - c : 1 - exp : out1*.jpg
file: out2b.jpg - c : 1 - exp : out1*.jpg
file: out1.jpg - c : 1 - exp : out1*.jpg
out1.jpg
file: out2a.jpg - c : 2 - exp : out2*.jpg
out2a.jpg
file: sofnm.py - c : 3 - exp : out3*.jpg
file: out42a.jpg - c : 3 - exp : out3*.jpg
file: out3.jpg - c : 3 - exp : out3*.jpg
out3.jpg

As you can see, your assumption that os.listdir() would return the files in a given order (starting with "out1.jpg" and ending with "out4.jpg") was wrong. As a general rule, when your code don't behave as you expect, tracing the code execution (and the relevant values) is most often the simplest way to find out why.

Upvotes: 4

Bhawan
Bhawan

Reputation: 2491

You are incrementing c after a file match, it is quite possible that file name is out2a.jpg but the value of c is 1. So, it will not match. You should either list all the files in ascending order so that out1 will come before out2 in listdir or you should use a generic numeric match instead of one by one like shown below:

import fnmatch
import os

for file in os.listdir('.'):
    #print(file)
    if fnmatch.fnmatch(file, 'out[0-9]*.jpg'):
        print(file)

Upvotes: 2

samwalton
samwalton

Reputation: 551

Running through this for loop, you're checking each file in the directory against a very specific file name (first out1*.jpg, then out2*.jpg) with no guarantee that the order of these files matches. When I tried to run the code locally for example, it first compared out2a.jpg with the pattern out1*.jpg, then out2b.jpg with out2*.jpg, then test.py (the script) with out3*.jpg.

You'd be better off using a module like glob (https://docs.python.org/3/library/glob.html) to search for 'out*.jpg': glob.glob('out[0-9]*.jpg').

Upvotes: 1

Related Questions