The fnmatch module matches filenames against shell-style wildcards. Unlike glob, it doesn't touch the filesystem—just pattern matching.
Basic Matching
import fnmatch
# Does filename match pattern?
fnmatch.fnmatch('data.txt', '*.txt') # True
fnmatch.fnmatch('data.txt', 'data.*') # True
fnmatch.fnmatch('data.txt', '*.py') # False
fnmatch.fnmatch('readme.md', 'READ*') # True (case-insensitive on Windows)Case Sensitivity
import fnmatch
# fnmatch: follows OS convention
# Case-insensitive on Windows, sensitive on Unix
fnmatch.fnmatch('DATA.txt', 'data.*') # True on Windows, False on Unix
# fnmatchcase: always case-sensitive
fnmatch.fnmatchcase('DATA.txt', 'data.*') # False
fnmatch.fnmatchcase('data.txt', 'data.*') # TruePattern Syntax
import fnmatch
# * matches everything
fnmatch.fnmatch('anything.txt', '*') # True
fnmatch.fnmatch('file.txt', '*.txt') # True
# ? matches single character
fnmatch.fnmatch('file1.txt', 'file?.txt') # True
fnmatch.fnmatch('file10.txt', 'file?.txt') # False
# [seq] matches characters in seq
fnmatch.fnmatch('file1.txt', 'file[123].txt') # True
fnmatch.fnmatch('file4.txt', 'file[123].txt') # False
# [!seq] matches characters NOT in seq
fnmatch.fnmatch('fileA.txt', 'file[!0-9].txt') # True
fnmatch.fnmatch('file1.txt', 'file[!0-9].txt') # FalseFiltering Lists
import fnmatch
files = ['data.csv', 'report.csv', 'image.png', 'notes.txt', 'backup.csv']
# Filter matching files
csv_files = fnmatch.filter(files, '*.csv')
# ['data.csv', 'report.csv', 'backup.csv']
# Filter with character class
r_files = fnmatch.filter(files, '[rn]*')
# ['report.csv', 'notes.txt']Translate to Regex
import fnmatch
import re
# Convert pattern to regex
regex = fnmatch.translate('*.txt')
print(regex) # '(?s:.*\\.txt)\\Z'
# Use compiled regex
pattern = re.compile(fnmatch.translate('*.txt'))
pattern.match('file.txt') # Match object
pattern.match('file.csv') # None
# Useful for complex matching
def multi_match(filename, patterns):
"""Check if filename matches any pattern."""
combined = '|'.join(fnmatch.translate(p) for p in patterns)
return re.match(combined, filename) is not None
multi_match('test.py', ['*.py', '*.txt']) # TruePractical Examples
Filtering Directory Listings
import fnmatch
import os
def find_files(directory, pattern):
"""Find files matching pattern in directory."""
matches = []
for filename in os.listdir(directory):
if fnmatch.fnmatch(filename, pattern):
matches.append(os.path.join(directory, filename))
return matches
python_files = find_files('.', '*.py').gitignore-Style Matching
import fnmatch
def is_ignored(filepath, patterns):
"""Check if path matches any ignore pattern."""
for pattern in patterns:
if fnmatch.fnmatch(filepath, pattern):
return True
# Also check basename
if fnmatch.fnmatch(os.path.basename(filepath), pattern):
return True
return False
ignore_patterns = ['*.pyc', '__pycache__', '.git', '*.log']
is_ignored('cache/__pycache__', ignore_patterns) # True
is_ignored('main.py', ignore_patterns) # FalseFile Type Categorization
import fnmatch
FILE_TYPES = {
'images': ['*.jpg', '*.jpeg', '*.png', '*.gif', '*.svg'],
'documents': ['*.pdf', '*.doc', '*.docx', '*.txt', '*.md'],
'code': ['*.py', '*.js', '*.ts', '*.go', '*.rs'],
'data': ['*.json', '*.csv', '*.xml', '*.yaml'],
}
def categorize_file(filename):
"""Return category for a filename."""
for category, patterns in FILE_TYPES.items():
for pattern in patterns:
if fnmatch.fnmatch(filename.lower(), pattern):
return category
return 'other'
categorize_file('photo.jpg') # 'images'
categorize_file('main.py') # 'code'
categorize_file('data.csv') # 'data'Exclude Filter
import fnmatch
def filter_exclude(names, include, exclude=None):
"""Filter names: include matching, exclude specified."""
result = fnmatch.filter(names, include)
if exclude:
result = [n for n in result if not fnmatch.fnmatch(n, exclude)]
return result
files = ['test_main.py', 'main.py', 'test_utils.py', 'utils.py']
filter_exclude(files, '*.py', 'test_*')
# ['main.py', 'utils.py']fnmatch vs glob vs re
| Feature | fnmatch | glob | re |
|---|---|---|---|
| Filesystem access | No | Yes | No |
| Pattern syntax | Shell | Shell | Regex |
| Complexity | Simple | Simple | Complex |
| Use case | Filter strings | Find files | Complex patterns |
# fnmatch: match strings against patterns
fnmatch.fnmatch('file.txt', '*.txt')
# glob: find actual files
import glob
glob.glob('*.txt')
# re: complex pattern matching
import re
re.match(r'.*\.txt$', 'file.txt')Quick Reference
import fnmatch
# Match single filename
fnmatch.fnmatch(name, pattern) # OS case rules
fnmatch.fnmatchcase(name, pattern) # Case-sensitive
# Filter list
fnmatch.filter(names, pattern)
# Convert to regex
fnmatch.translate(pattern)| Pattern | Matches |
|---|---|
* | Everything |
? | Single character |
[seq] | Any char in seq |
[!seq] | Any char not in seq |
[a-z] | Range |
fnmatch is pure pattern matching without filesystem access. Use it when you have filenames as strings and need to filter them.
React to this post: