Skip to content

How to Understand '\n' vs '\r' in Python: Newline vs Carriage Return

Learn how (newline) moves the cursor down while (carriage return) returns it to the start, and when to use each for cross-platform text handling, terminal updates, and network protocols in Python.

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
World

Character-by-Character Breakdown

text = "ABC\rXY"


print(text)  # XYC

Output:

XYC

The \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 done

Output (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 text

Cross-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.

Navigation