The str.isprintable() method returns True if all characters in the string are considered printable according to the Unicode standard, or if the string is empty. It returns False if the string contains escape sequences, control characters (such as \n, \t, \r, or \x00), or formatting marks.
Empty String & Whitespace Rule: Standard space (" ", ASCII 32) is considered printable. Unlike isalpha() or isdigit(), an empty string "" evaluates to True because it contains zero non-printable characters.
The isprintable() method requires no arguments and evaluates string contents directly:
is_safe_text = string.isprintable()
| Parameter | Type | Description |
|---|---|---|
| None | — | Takes zero arguments. Passing parameters raises TypeError. |
| Return Value | bool |
Returns True if all characters are printable or if string is empty; otherwise False. |
Characters that produce visual glyphs (letters, numbers, punctuation, and standard spaces) are printable. Control escape sequences that manipulate output position or hardware devices evaluate to False:
# Printable strings (return True)
print("Welcome to plus2net".isprintable()) # True (letters + space)
print("Price: $99.95!".isprintable()) # True (symbols + punctuation)
print(" ".isprintable()) # True (regular spaces are printable)
print("".isprintable()) # True (empty string is printable)
# Non-printable control characters (return False)
print("Hello\tWorld".isprintable()) # False: horizontal tab \t
print("Line 1\nLine 2".isprintable()) # False: newline \n
print("Line\r\n".isprintable()) # False: carriage return \r
print("Null\x00Byte".isprintable()) # False: null byte \x00
print("Bell\x07Alert".isprintable()) # False: terminal bell \x07
Output:
True
True
True
True
False
False
False
False
False
\n and \t alter the visual layout of text on a screen or printer, they do not occupy a standalone printed glyph cell; they are control instructions to move the cursor. Hence, Python correctly classifies them as non-printable.
Under the Unicode standard, characters are assigned General Categories. Python's str.isprintable() classifies a character as printable unless its category is:
Cc (Other, Control): e.g. \x00 through \x1F, \x7F through \x9F.Cf (Other, Format): e.g. soft hyphen, zero-width joiners.Cs (Other, Surrogate): code points reserved for UTF-16 surrogates.Co (Other, Private Use) & Cn (Other, Not Assigned).Zl (Separator, Line: e.g. \u2028) & Zp (Separator, Paragraph: e.g. \u2029).Notice that Zs (Separator, Space — such as ASCII space " ") is explicitly excluded from the non-printable filter, making normal spaces printable:
import unicodedata
chars = ["A", " ", "\t", "\n", "\x00", "€", "中"]
print(f"{'Char':<6} | {'Repr':<8} | {'Category':<10} | {'isprintable':<12}")
print("-" * 45)
for c in chars:
cat = unicodedata.category(c)
print(f"{c:<6} | {repr(c):<8} | {cat:<10} | {str(c.isprintable()):<12}")
Output:
Char | Repr | Category | isprintable
---------------------------------------------
A | 'A' | Lu | True
| ' ' | Zs | True
| '\t' | Cc | False
| '\n' | Cc | False
\x00 | '\x00' | Cc | False
€ | '€' | Sc | True
中 | '中' | Lo | True
Modern web security exploits frequently employ invisible characters and ANSI terminal codes. str.isprintable() easily catches both:
# 1. Zero-width space (\u200b): used for fingerprinting and evading filters
clean_text = "admin"
spoofed_text = "ad\u200bmin" # Looks visually identical on screen
print("Clean visible text: ", clean_text.isprintable()) # True
print("Spoofed zero-width: ", spoofed_text.isprintable()) # False
# 2. ANSI terminal color escape sequence (\x1b[31m)
colored_text = "\x1b[31mERROR\x1b[0m"
print("Colored terminal text:", colored_text.isprintable()) # False (\x1b is escape)
Output:
Clean visible text: True
Spoofed zero-width: False
Colored terminal text: False
The table below summarizes how Python's character inspection methods behave across various string types:
| String | isprintable() |
isspace() |
isascii() |
isalnum() |
|---|---|---|---|---|
"Hello" | True | False | True | True |
"Hello World" | True | False | True | False (has space) |
" " (spaces) | True | True | True | False |
"\t" (tab) | False | True | True | False |
"\n" (newline) | False | True | True | False |
"Café" | True | False | False (non-ASCII) | True |
"" (empty) | True | False | True | False |
When logging external user inputs, malicious actors can inject terminal escape codes (like cursor resets or color changes) or carriage returns to rewrite log lines. Sanitizing with isprintable() neutralizes this attack vector:
def safe_log_message(raw_user_input):
if raw_user_input.isprintable():
return raw_user_input
# Escape non-printable control characters safely using repr or ASCII escaping
return "".join(ch if ch.isprintable() else repr(ch)[1:-1] for ch in raw_user_input)
malicious_payload = "Payment failed\r[INFO] Admin logged in successfully!"
print("Sanitized log entry:")
print(safe_log_message(malicious_payload))
Output:
Sanitized log entry:
Payment failed\r[INFO] Admin logged in successfully!
When reading external CSV or JSON data, improperly decoded binary files often contain embedded null bytes (\x00) or control codes. You can flag bad records before saving them to a database:
records = [
{"id": 1, "comment": "Excellent service and fast response."},
{"id": 2, "comment": "Corrupted record\x00with null byte."},
{"id": 3, "comment": "Clean feedback."}
]
clean_records = [r for r in records if r["comment"].isprintable()]
print(f"Accepted {len(clean_records)} of {len(records)} records.")
Output:
Accepted 2 of 3 records.
The following production script implements an audit cleaner that detects invisible characters, escapes control codes, and validates text streams with unit assertions:
class TextSecurityAuditor:
@staticmethod
def is_clean_single_line(text):
# Must be printable and contain no control chars or line breaks
if not isinstance(text, str):
return False
return text.isprintable()
@staticmethod
def sanitize_for_display(text, replacement="?"):
# Replaces any non-printable characters with safe replacement
if not isinstance(text, str):
return ""
return "".join(ch if ch.isprintable() else replacement for ch in text)
@staticmethod
def find_non_printable_codepoints(text):
# Returns list of hex codepoints for any non-printable character
return [hex(ord(ch)) for ch in text if not ch.isprintable()]
# --- Unit Assertions and Verification ---
auditor = TextSecurityAuditor()
# 1. Validation tests
assert auditor.is_clean_single_line("User logged in from 192.168.1.1") is True
assert auditor.is_clean_single_line("") is True # Empty string is printable
assert auditor.is_clean_single_line("Hello\nWorld") is False
assert auditor.is_clean_single_line("Data\tTab") is False
assert auditor.is_clean_single_line("User\x00Name") is False
# 2. Unicode and international printability
assert auditor.is_clean_single_line("Hello 世界") is True
assert auditor.is_clean_single_line("Hidden\u200bZeroWidth") is False
# 3. Sanitizer and codepoint inspection
raw_str = "Login\tSuccess\x00!"
sanitized = auditor.sanitize_for_display(raw_str, replacement="_")
assert sanitized == "Login_Success_!"
assert auditor.find_non_printable_codepoints(raw_str) == ['0x9', '0x0']
print("Clean line test: ", auditor.is_clean_single_line("Clean string"))
print("Sanitized output: ", sanitized)
print("Detected bad codepoints:", auditor.find_non_printable_codepoints(raw_str))
print("All str.isprintable() unit assertions passed successfully!")
Open in Google Colab View on GitHub
Run all printable character evaluations interactively, inspect control characters, sanitize terminal log streams, and detect invisible zero-width spaces in Google Colab.
Author & Instructor at plus2net
I write and maintain practical tutorials on Python, PHP, SQL, JavaScript, HTML, jQuery, and web development at plus2net. The tutorials focus on clear explanations, working examples, and code that readers can test and adapt while learning.