These two escape sequences control cursor positioning differently: \n moves the cursor down to a new line, while \r returns it to the beginning of the current line. Understanding this distinction is essential for cross-platform compatibility and terminal manipulation.
Core Differences
| Character | Name | Action | Primary OS Usage |
|---|---|---|---|
\n |
Line Feed (LF) | Move cursor down one line | Linux, macOS |
\r |
Carriage Return (CR) | Move cursor to line start | Classic Mac OS (pre-OS X) |
\r\n |
CRLF | Return to start, then new line | Windows |
The terminology comes from typewriters: the carriage return moved the typing mechanism back to the left margin, while the line feed advanced the paper up one line.
Visualizing the Difference
print("Line 1\nLine 2\nLine 3")
print("Hello\rWorld")Output:
Line 1
Line 2
Line 3
WorldCharacter-by-Character Breakdown
text = "ABC\rXY"
print(text) # XYCOutput:
XYCThe \r Trick: Dynamic Terminal Updates
The carriage return enables in-place updates, perfect for progress indicators and status displays.
Simple Progress Bar
import time
for i in range(1, 6):
print(f"\rProgress: {i * 20}%", end="", flush=True)
time.sleep(0.5)
print("\nComplete!")Output (animated, updating in place):
Progress: 100%
Complete!Visual Progress Bar
import time
def progress_bar(current, total, width=40):
percent = current / total
filled = int(width * percent)
bar = '█' * filled + '░' * (width - filled)
print(f"\r[{bar}] {percent:.1%}", end="", flush=True)
for i in range(101):
progress_bar(i, 100)
time.sleep(0.03)
print() # Move to new line when doneOutput (the progress bar updates from 0% to 100%):
[████████████████████████████████████████] 100.0%Countdown Timer
import time
for seconds in range(10, 0, -1):
print(f"\rStarting in {seconds} seconds... ", end="", flush=True)
time.sleep(1)
print("\rLaunching now! ") # Extra spaces clear previous textCross-Platform File Handling
Different operating systems use different line ending conventions. Python handles this automatically in text mode.
Automatic Normalization
with open("data.txt", "r") as f:
content = f.read()
with open("output.txt", "w") as f:
f.write("Line 1\nLine 2")Preserving Original Line Endings
with open("data.txt", "r", newline='') as f:
content = f.read()Forcing Specific Line Endings
with open("unix_file.txt", "w", newline='\n') as f:
f.write("Line 1\nLine 2")
with open("windows_file.txt", "w", newline='\r\n') as f:
f.write("Line 1\nLine 2")Converting Between Line Endings
def convert_line_endings(text, target='unix'):
"""Convert line endings to specified format."""
normalized = text.replace('\r\n', '\n').replace('\r', '\n')
if target == 'unix':
return normalized
elif target == 'windows':
return normalized.replace('\n', '\r\n')
elif target == 'classic_mac':
return normalized.replace('\n', '\r')
else:
raise ValueError(f"Unknown target: {target}")
text = "Line 1\r\nLine 2\rLine 3\n"
print(repr(convert_line_endings(text, 'unix')))
print(repr(convert_line_endings(text, 'windows')))Detecting Line Ending Style
def detect_line_ending(text):
"""Detect the predominant line ending style."""
crlf_count = text.count('\r\n')
cr_count = text.count('\r') - crlf_count # Exclude \r in \r\n
lf_count = text.count('\n') - crlf_count # Exclude \n in \r\n
counts = {
'Windows (CRLF)': crlf_count,
'Unix (LF)': lf_count,
'Classic Mac (CR)': cr_count
}
if max(counts.values()) == 0:
return "No line endings found"
return max(counts, key=counts.get)
print(detect_line_ending("line1\r\nline2\r\n")) # Windows (CRLF)
print(detect_line_ending("line1\nline2\n")) # Unix (LF)Network Protocols and \r\n
Many internet protocols require CRLF line endings:
http_request = "GET / HTTP/1.1\r\nHost: example.com\r\n\r\n"
smtp_message = "MAIL FROM:<sender@example.com>\r\n"
def send_line(socket, message):
socket.send(f"{message}\r\n".encode())Spinner Animation
Another practical use of \r:
import time
import itertools
def spinner(duration=3):
"""Display a spinning animation."""
frames = itertools.cycle(['⠋', '⠙', '⠹', '⠸', '⠼', '⠴', '⠦', '⠧', '⠇', '⠏'])
end_time = time.time() + duration
while time.time() < end_time:
print(f"\r{next(frames)} Loading...", end="", flush=True)
time.sleep(0.1)
print("\r✓ Complete! ")
spinner(2)Summary Table
| Use Case | Character | Example |
|---|---|---|
| Standard new lines | \n |
print("Line 1\nLine 2") |
| Overwrite current line | \r |
print(f"\r{status}", end="") |
| Windows files | \r\n |
Network protocols, legacy systems |
| Cross-platform reading | Default | open(file, "r") handles all |
| Progress indicators | \r |
Live updating status bars |
Use \n for nearly all standard text output. Reserve \r for terminal tricks like progress bars and spinners where you want to update content in place without scrolling.