Why Python Fnmatch Library is a Game-Changer for File Management

Python provides us with an easy way to deal with Unix shell-style wildcards by giving us access to the fnmatch library. It is utilized for multiple things like:

  • Filtering filenames,
  • Searching for files, and
  • Validating user input.

The output is always in the form of boolean TRUE or FALSE.

Wildcards used with fnmatch library

python fnmatch

These are some of the widely used wildcards used with fnmatch library.

WILDCARDSUSE
?any single character.
*any sequence of characters
[sequence]any character in the sequence
[!sequence]any character not in the sequence 

Commonly used fnmatch functions

The fnmatch module has these four primary functions:

  1. fnmatch.fnmatch(filename, pattern)
  2. fnmatch.fnmatchcase(filename, pattern)
  3. fnmatch.filter(names, pattern)
  4. fnmatch.translate(pattern)

Usage of fnmatch.fnmatch()

It is used to match filenames with the given pattern. The case of letters is dependent on the operating system. Usually, it is changed to similar case for os that is not case sensitive. In this example, you can obtain all file names ending with .txt i.e. a;; text files.

import fnmatch
filenames = ["abc.txt", "ddd.py", "c.txt"]
ans = [i for i in filenames if fnmatch.fnmatch(i, "*.txt")]
print(ans)

Let’s have a look at the output:

['abc.txt', 'c.txt']

Usage of fnmatch.fnmatchcase()

This fnmatch extension is case sensitive. In this example, you can try matching csv files files starting with “data”. Say, after executing this, you may get output as: data.csv, data1.csv , data12.csv and so on.

import fnmatch
import os
pattern = 'data*.csv'  
print(f'Pattern: {pattern}\n')
files = os.listdir('.')
for i in files:
    match = fnmatch.fnmatchcase(i, pattern)
    print(f'Filename: {i:<25} Match: {match}')

Usage of fnmatch.filter()

It returns a list of files altogether that match the given pattern. There is no need of re-iterating over the files one by one. This example provides a pattern to match files with a .jpg extension.

import fnmatch
import os
pattern = '*.jpg'  
files = os.listdir('.')
matches = fnmatch.filter(files, pattern)
print(f'Filenames matching the pattern: {matches}')

Usage of fnmatch.translate()

This converts a pattern into a regular expression using translate() function which can be used for file matching too.

import fnmatch
import os
import re

pattern = 'file*.txt'
regexp = fnmatch.translate(pattern)
#to compile
reobj = re.compile(regex_pattern)

files = os.listdir('.')
# regex for file matching
ans = [i for i in files if reobj.match(i)]
print(f'Translated Pattern : {regexp}')
print(f'Matched Files: {ans}')

fnmatch for files’ searching

You can use fnmatch to search for specific files. This example prints a list of all files in the current directory that start with the letter a:

import fnmatch
import os
cwd = os.getcwd()

#to store matched files
ans = []                    
for i in os.listdir(cwd):
    if fnmatch.fnmatch(i, "a*"):
        ans.append(i)
print(ans)

And the output will be:

['abc.txt']

fnmatch for validating user input

If you wish to validate user input, you may use fnmatch. For example, we can specify a file extension and check if a user input ends with a particular extension.

import fnmatch
def Validation(file):
    if not fnmatch.fnmatch(file, "*.exe"):
        raise ValueError("filename must end with the .exe extension.")

# user input 
f = input("Enter a filename: ")

# for Validation
Validation(f)

python fnmatch regex

Apart from the aforementioned examples, you can use regex in multiple ways for pattern matching for files. You may use any combination of numbers or wildcard characters while using the translate function of fnmatch library.

regexp = re.compile('_([0-9-_.]+\...)')
files=[]
for i in os.listdir(cwd):
    if fnmatch.fnmatch(i, regexp):
        files.append(i)

python fnmatch exclude pattern

You can use the if not phrase with the pattern in order to exclude a part of it.

import fnmatch
files = ["file1.txt", "file2.jpg", "file3.txt", "file4.doc"]
# Pattern to exclude
exclude_pattern = "*.txt"
# Use fnmatch.filter to exclude files 
Ans = [i for i in files if not fnmatch.fnmatch(i, exclude_pattern)]
print(Ans)

python fnmatch vs glob python

Python’s glob module can perform more complex pattern matching as compared to python fnmatch. So, fnmatch works for single strings well but glob can execute pattern matching in directories too.

import os
import glob
def func(dirname, extensions):
    #i as filename
    for i in glob.glob(dirname):
        base, ext = os.path.splitext(i)
        if ext.lower() in extensions:
            print i
func('dir', ['.jpeg', '.png', '.jpg'])

In this example, first we need to pass the directory name as parameter to the func function. It will check all the files in given directory. glob.glob() has been used for the same. os.path.splitext() checks file entensions and the ones that are matched are printed.

In addition to this, fnmatch usually has boolean values as output unless we are looping all the files and after matching, appending them to a list. This displays the files that have matched. However, with glob we directly obtain a list of files.

Let’s have a look at these differences in a consolidated format:

fnmatchglob
It can do easy pattern matchingIt can handle complex pattern matching
It can’t do pattern matching in directoriesIt can do pattern matching in directories
It is simpleIt is quite complex

Ignore case in python fnmatch

For ignoring the case, one can use the filter extension of fnmatch library. In the given piece of code, all the .py files i.e. files with python code will be selected. Filter is an extremely important extension of python fnmatch module.

import os
from fnmatch import filter
filter(os.listdir('.'), '*.[Pp][Yy]')

python fnmatch digits

One can make use of python fnmatch library to match digits in a file too. One can execute this in two ways: either digits + some characters with the file name or a filename with digits only. An example of the first one can be:

pattern = 'abc_def_[0-9]*.json'

For the 2nd case, you can specify the name of directory with the exact location and specify the pattern which comprises of digits from 0 to 9.

import fnmatch
import os

dir = 'dirname' 
pattern = '*[0-9]*'  

# List all files in the specified directory
files = os.listdir(dir)

# Filter files that match the pattern
res = [i for i in files if fnmatch.fnmatch(i, pattern)]

# Print the matching filenames
for j in res:
    print(j)
#i, j refer to filenames

python fnmatch multiple patterns

You can match multiple patterns using fnmatch ‘s filter extension. You can specify a set of patterns in order to match them.

import fnmatch
#p for patterns
p = ["*.py", "*.txt"]
path= ["src/main.py", "src/README.md"]

Ans = fnmatch.filter(path, p)
# output will be :
# ["src/main.py"]

FAQS

What is the difference between fnmatch.fnmatch() and fnmatch.filter()?

The first one deals with one file at a time whereas the filter function displays a list of files that match the pattern.

Which special characters can be used with fnmatch library?

Special characters like ? (Question Mark), * (Asterisk), [] (Square Brackets), {} (Curly Braces), [!] or [^] (Exclamation Mark or Caret), etc can be used.

Can you use fnmatch with recursive matching?

No, fnmatch doesn’t support recursive matching.

Conclusion

Through this article, you must have comprehended the usage of Python fnmatch in detail. It covers four important functions of fnmatch along with their applications in Python. Apart from this, this blog also tells us how we can differentiate between Python fnmatch and the widely used glob module.

Subscribe
Notify of
guest
0 Comments
Inline Feedbacks
View all comments