Validating file names is a critical step in Python applications that generate files or accept user uploads. Operating systems (Windows, Linux, macOS) have different rules regarding forbidden characters, length limits, and reserved keywords. Failing to validate these names can lead to OSError, data overwrites, or security vulnerabilities like directory traversal attacks.
This guide covers how to validate file names using standard Regular Expressions and the robust third-party library pathvalidate.
Understanding OS Naming Constraints
Before writing code, it is essential to know what makes a filename "invalid."
- Windows: Prohibits
< > : " / \ | ? *. Also reserves names likeCON,PRN,AUX,NUL,COM1-COM9,LPT1-LPT9. - Linux/macOS: Generally allows almost anything except the forward slash
/(path separator) and the null byte\0. - Length: Most file systems limit filenames to 255 bytes.
Method 1: Using Regular Expressions (Custom Rules)
If you need to enforce a specific naming convention (e.g., "only alphanumeric characters and underscores"), Regular Expressions (regex) are the standard tool.
Allow-list Approach (Whitelist)
This approach is safer than trying to ban specific characters. We define exactly what is allowed.
import re
def is_valid_filename(filename):
pattern = r'^[a-zA-Z0-9_\-\.]+$'
if re.match(pattern, filename) and ".." not in filename:
return True
return False
filenames = ["report_2023.txt", "image/png", "user?name", "../hack"]
for fname in filenames:
if is_valid_filename(fname):
print(f"✅ '{fname}' is valid.")
else:
print(f"⛔️ '{fname}' is invalid.")Output:
✅ 'report_2023.txt' is valid.
⛔️ 'image/png' is invalid.
⛔️ 'user?name' is invalid.
⛔️ '../hack' is invalid.Sanitizing Filenames
Instead of rejecting the input, you might want to remove invalid characters.
import re
def sanitize_filename_regex(filename):
cleaned = re.sub(r'[^a-zA-Z0-9_\-\.]', '_', filename)
return cleaned
raw_name = "My<Cool>File:v1.txt"
clean_name = sanitize_filename_regex(raw_name)
print(f"Original: {raw_name}")
print(f"Cleaned: {clean_name}")Output:
Original: My<Cool>File:v1.txt
Cleaned: My_Cool_File_v1.txtMethod 2: Using the pathvalidate Library (Recommended)
Handling every edge case (like Windows reserved names or cross-platform rules) manually is difficult. The pathvalidate library is the industry standard for this task.
Installation:
pip install pathvalidateValidating and Sanitizing
from pathvalidate import is_valid_filename, sanitize_filename
filename_bad = "config:v1/final?.json"
filename_good = "config_v1_final.json"
print(f"Is '{filename_bad}' valid? {is_valid_filename(filename_bad)}")
print(f"Is '{filename_good}' valid? {is_valid_filename(filename_good)}")
sanitized = sanitize_filename(filename_bad)
print(f"Sanitized: {sanitized}")Output:
Is 'config:v1/final?.json' valid? False
Is 'config_v1_final.json' valid? True
Sanitized: configv1final.jsonMethod 3: Checking for Reserved Filenames
If you are not using pathvalidate and plan to support Windows users, you must explicitly check for reserved device names. Windows will not allow files named CON, PRN, etc., even with an extension (e.g., CON.txt is invalid).
import os
def is_reserved_name(filename):
reserved_names = {
"CON", "PRN", "AUX", "NUL",
"COM1", "COM2", "COM3", "COM4", "COM5", "COM6", "COM7", "COM8", "COM9",
"LPT1", "LPT2", "LPT3", "LPT4", "LPT5", "LPT6", "LPT7", "LPT8", "LPT9"
}
base_name = os.path.splitext(filename)[0].upper()
return base_name in reserved_names
files = ["data.txt", "con.txt", "aux"]
for f in files:
if is_reserved_name(f):
print(f"⛔️ '{f}' is a reserved system name.")
else:
print(f"✅ '{f}' is okay to use.")Output:
✅ 'data.txt' is okay to use.
⛔️ 'con.txt' is a reserved system name.
⛔️ 'aux' is a reserved system name.Conclusion
To ensure your file names are valid and safe:
- Use
pathvalidatefor production applications. It covers cross-platform edge cases that manual regex often misses. - Use Whitelist Regex (
r'^[a-zA-Z0-9_\-\.]+$') if you need to enforce a strict naming convention within your app. - Sanitize, don't just validate. It is usually better to auto-correct a bad filename (by replacing bad characters) than to crash the program.
- Block Directory Traversal. Always ensure filenames do not contain
..or path separators (/,\) if they are supposed to be simple filenames.