GICT knowledge base
Python Escape Characters: Strings, Raw Literals and Regex
Published
In a Python string literal, a backslash can introduce an escape sequence such as \n for a newline or \\ for a literal backslash. Distinguish the source spelling, the resulting string value and the way a terminal or browser displays that value.
The examples below were executed with Python 3.12.14. They use repr(), lengths and code-point checks to inspect control characters without assuming a particular terminal will beep, move a cursor or erase text.
Compare an actual newline with a backslash followed by n
print() writes a string’s contents. repr() returns a diagnostic representation that makes many invisible characters visible through escape notation. The representation is not a count of the characters in the underlying value.
newline = "A\nB"
literal = "A\\nB"
assert len(newline) == 3
assert len(literal) == 4
assert newline.splitlines() == ["A", "B"]
assert literal == r"A\nB"
assert repr(newline) == "'A\\nB'"
print(repr(newline), repr(literal))
Common escapes and their limits
| Source spelling | Character represented | Important distinction |
|---|---|---|
\\ |
Backslash | Two backslashes in this ordinary literal represent one character. |
\', \" |
Quote | Escaping can keep a quote from ending the literal. |
\n |
Line feed | Newline handling can also depend on the output stream. |
\t |
Horizontal tab | Display width depends on tab stops; it is not a fixed number of spaces. |
\r |
Carriage return | A terminal may move to the beginning of a line; the string is not erased. |
\b |
Backspace | A control character, not deletion of the preceding character from the string. |
\a, \f, \v |
Bell, form feed, vertical tab | Rendering or audible behavior depends on the receiving environment. |
backspaced = "Helloo\b"
assert len(backspaced) == 7
assert backspaced[:-1] == "Helloo"
assert backspaced != "Hello"
controls = "\t\r\b\a\f\v"
assert [ord(character) for character in controls] == [9, 13, 8, 7, 12, 11]
assert len("A\tB") == 3
print(repr(backspaced), repr(controls))
To remove data, use an actual string operation with a clearly defined rule, not a terminal control character. Do not print untrusted text containing terminal controls into an administrative terminal without considering how it will be rendered.
Choose quotes and spell backslashes deliberately
Using double quotes around text containing an apostrophe often avoids an unnecessary escape. Windows-style path literals need particular care because sequences such as \t and \U have meaning to the Python parser. The paths below are strings only; the example opens no files.
assert "It's ready" == 'It\'s ready'
assert 'He said "ready"' == "He said \"ready\""
ordinary_path = "C:\\Users\\Example\\Documents"
raw_path = r"C:\Users\Example\Documents"
assert ordinary_path == raw_path
assert type(raw_path) is str
assert "\t" in "C:\temp"
assert "\t" not in r"C:\temp"
print(repr(raw_path))
Raw strings still have lexical rules
The r prefix changes how the literal is parsed; it does not create a special runtime string type. A raw literal cannot end with an odd number of backslashes because the closing quote would be escaped. Quotes still need valid delimiting, and a backslash used before a quote remains in the resulting raw string.
This block constructs intentionally invalid source as data, then confirms the parser rejects it. It compiles only these fixed examples, not user-supplied code.
raw_source_with_trailing_slash = 'path = r"C:\\folder\\"'
ordinary_source_with_bad_unicode_escape = 'path = "C:\\Users\\Example"'
for source in [raw_source_with_trailing_slash,
ordinary_source_with_bad_unicode_escape]:
try:
compile(source, "invalid-string-example", "exec")
except SyntaxError:
pass
else:
raise AssertionError("Invalid string literal was accepted")
path_with_trailing_slash = r"C:\folder" + "\\"
assert path_with_trailing_slash.endswith("\\")
assert r"\"" == "\\\""
print(repr(path_with_trailing_slash))
Keep Unicode escapes separate from encodings
In a str literal, \u uses exactly four hexadecimal digits and \U uses exactly eight to specify a Unicode code point. \x uses two hexadecimal digits. These are source notations, not promises of UTF-16, UTF-32 or a terminal’s display encoding.
copyright_symbol = "\u00a9"
face = "\U0001f600"
assert ord(copyright_symbol) == 0xA9
assert len(face) == 1
assert "\x41" == "A"
assert copyright_symbol.encode("utf-8") == b"\xc2\xa9"
assert len("e\u0301") == 2
print(ascii(copyright_symbol), ascii(face))
Python 3 source normally defaults to UTF-8, but the escapes above contain only ASCII source characters. Displaying the resulting value depends separately on the output encoding and available glyphs. A user-perceived character can consist of several code points, so len() is not always a count of visible symbols.
Remember the regular-expression parser is a second layer
A raw string lets a backslash reach the regex engine without first being interpreted as a Python escape. The regex engine can still interpret it: r"\n" matches a newline in a pattern. For Unicode string patterns, \d includes Unicode decimal digits; use [0-9] or the appropriate ASCII flag when only ASCII digits are intended.
import re
assert re.fullmatch(r"\d+", "123") is not None
assert re.fullmatch(r"\d+", "\u0661") is not None
assert re.fullmatch(r"[0-9]+", "\u0661") is None
assert re.fullmatch(r"\n", "\n") is not None
assert re.fullmatch(r"\n", r"\n") is None
literal_name = "report[1].txt"
assert re.fullmatch(re.escape(literal_name), literal_name) is not None
assert re.fullmatch(re.escape(literal_name), "report1Xtxt") is None
print("Regex layer checks passed")
re.escape() prepares literal text for a regex pattern. It is not escaping for SQL, HTML, shell commands or regex replacement strings. Use the appropriate parameterization or escaping mechanism for the destination context rather than adding backslashes indiscriminately.
Multi-line strings preserve their actual whitespace
Triple-quoted literals can contain real line breaks and indentation. They still interpret escapes unless they also have a raw prefix. Tabs are not a reliable way to align columns across every viewer; format a table deliberately if alignment matters.
with_escapes = "web\nmail\ndns"
with_lines = """web
mail
dns"""
assert with_escapes == with_lines
indented = """web
mail"""
assert indented.splitlines() == ["web", " mail"]
assert r"""web\nmail""" == "web\\nmail"
print(repr(with_lines))
Verification: All seven blocks ran independently. Tests cover exact lengths and code points, quote/path equivalence, two expected parser errors, raw-string behavior, Unicode encoding, regex interpretation and preserved indentation. No terminal cursor, sound or font-rendering behavior is claimed.
References: Python string literal rules and regular-expression syntax. Related: built-in inspection and conversion functions.
Related guides
How to Deploy a FastAPI App with Gunicorn and Nginx
Deploy FastAPI behind Nginx with Gunicorn and systemd. Separate application packages, check HTTPS and verify worker replacement and application health.
PythonLearn How to Modify Python Dictionary Values Easily
Learn how to modify Python dictionary values effectively with various methods, including direct assignment, update(), and comprehensions, along with common mistakes to...
PythonPython Set Copy Tutorial: Avoid Common Mistakes and Data Errors
Learn how to copy sets in Python safely and efficiently while avoiding common mistakes. Explore methods like copy(), set(), and deepcopy().
PythonDifferent Methods to Loop Through Sets in Python (Beginner-Friendly Guide)
Learn diverse methods to loop through sets in Python, including practical examples and use cases, ideal for beginners looking to master set operations.
