Carriage Return in Python: Newlines, splitlines(), and Output

Quick answer: In Python, carriage return is written as \r. In text it is a line-boundary character; in terminal output it returns the cursor to the start of the current line, which can support progress displays. Use splitlines() for mixed line endings, normalize deliberately, and open CSV files with newline=” so the csv module controls record endings.

Python Pool infographic comparing carriage return line feed CRLF splitlines normalization and terminal progress output
Treat carriage return as a line-boundary character in text or a cursor-control character in a terminal, and choose file newline handling deliberately.

A carriage return in Python is written as \r. It is a control character that moves the cursor back to the start of the current line. In text files, it can also appear as part of Windows line endings, where each line ends with \r\n. Modern Python code usually treats it as a normal character unless a file reader, terminal, or protocol gives it special meaning.

The official Python escape sequence reference lists \r with the other string escapes. The open() documentation explains newline handling when reading and writing files, and the csv documentation shows why CSV files are commonly opened with newline="".

Most Python programs meet carriage returns in one of three places. First, a string literal may include \r directly. Second, a file created on another platform may contain \r\n line endings. Third, terminal output may use \r to redraw progress on a single line. Each case has a different best practice, so the safest first step is to inspect the text with repr().

Do not guess from how the text appears on screen. A carriage return may overwrite the start of a terminal line, hide earlier text, or be converted by a file reader. Use repr(), byte inspection, or explicit newline settings when the exact characters matter.

See A Carriage Return In A String

The easiest way to understand \r is to compare the printed text with its representation. The representation shows the escape clearly, while normal printing lets the terminal interpret it.

text = "first\rsecond"

print(repr(text))
print(text)

The first line prints the visible escape. The second line may show only part of the text, depending on the terminal, because the cursor returns to the start before writing the later characters.

This behavior is useful for demos, but it can be confusing in logs. When debugging text from files, APIs, or copy-paste input, prefer repr(text) so hidden characters are visible.

Split Text With Mixed Line Endings

Python strings provide splitlines(), which understands common line break forms including \n, \r\n, and lone \r. It is often cleaner than splitting only on "\n".

text = "alpha\rbeta\r\ngamma\ndelta"

for line in text.splitlines():
    print(line)

This prints four logical lines. The method handles mixed endings without leaving stray carriage returns at the end of some entries.

Use splitlines() when you need a list of lines from an in-memory string. If you are reading from a file, Python’s text mode may already normalize line endings unless you ask it to preserve them.

Python Pool infographic comparing CR, LF, CRLF, platforms, and file bytes
Line endings: CR, LF, CRLF, platforms, and file bytes.

Normalize Carriage Returns

When text will be compared, stored, or displayed in a web page, normalize line endings first. A common policy is to convert every line break form to \n.

def normalize_lines(text):
    text = text.replace("\r\n", "\n")
    text = text.replace("\r", "\n")
    return text

sample = "red\rgreen\r\nblue"
print(normalize_lines(sample).split("\n"))

The order matters. Replace \r\n before replacing lone \r, otherwise the Windows pair becomes two line breaks instead of one.

This helper is useful after reading text from old exports, copied terminal output, form submissions, or small data feeds. If a protocol requires exact line endings, keep a separate path for that format instead of normalizing blindly.

Read A File And Preserve Endings

By default, text mode can translate different line endings to \n. If you need to detect carriage returns exactly, pass newline="" to open() or Path.open().

from pathlib import Path
from tempfile import TemporaryDirectory

with TemporaryDirectory() as folder:
    path = Path(folder) / "notes.txt"
    path.write_bytes(b"one\r\ntwo\rthree\n")

    with path.open("r", encoding="utf-8", newline="") as handle:
        text = handle.read()

print(repr(text))
print(text.count("\r"))

This keeps the original line break characters in the string. That makes it possible to count, report, or repair them with full control.

Use this approach for cleanup tools and format diagnostics. For ordinary reading, the default newline translation is often friendlier because every platform produces the same \n line separator in memory.

Python Pool infographic showing Python print, end, cursor movement, and visible output
Text output: Python print, end, cursor movement, and visible output.

Use Carriage Return For Progress Output

In terminal programs, \r can return the cursor to the start of the current line so the next print updates the same row. This is commonly used for progress messages.

for percent in (0, 25, 50, 75, 100):
    print(f"\rprogress: {percent:3d}%", end="")

print()

The final empty print() moves to the next line after the progress display is complete. Without it, the next output might appear on the same row.

This pattern is best for command-line tools that write to an interactive terminal. In log files, carriage returns can make output hard to read, so a normal line per status update is usually better.

Handle CSV Files Correctly

The csv module has its own newline handling. The documented pattern is to open files with newline="", which lets the module handle record endings itself.

import csv
from pathlib import Path
from tempfile import TemporaryDirectory

rows = [["name", "score"], ["Ada", "10"], ["Grace", "9"]]

with TemporaryDirectory() as folder:
    path = Path(folder) / "scores.csv"
    with path.open("w", encoding="utf-8", newline="") as handle:
        writer = csv.writer(handle)
        writer.writerows(rows)

    with path.open("r", encoding="utf-8", newline="") as handle:
        loaded = list(csv.reader(handle))

print(loaded)

This avoids accidental blank rows on some platforms and keeps CSV parsing consistent. It is a small option, but it prevents many line ending surprises in export and import scripts.

The practical rule is simple: use repr() to reveal carriage returns, splitlines() for mixed in-memory text, explicit normalization when web or comparison output needs consistent newlines, and newline="" when a file format needs exact control.

Carriage return in Python is not a special data type or a mystery feature. It is one character with different effects depending on where it is used. Once you separate terminal behavior from file newline behavior, the fixes are predictable and small.

Python Pool infographic comparing splitlines, universal newlines, and line boundaries
Split lines: Splitlines, universal newlines, and line boundaries.

Reveal The Character With repr

Printing text can hide control characters, so use repr() when diagnosing line endings. Count carriage returns separately when you need to distinguish old Mac-style text, Unix LF, and Windows CRLF input.

sample = "one\rtwo\r\nthree\n"

print(repr(sample))
print("carriage returns:", sample.count("\r"))
print("line feeds:", sample.count("\n"))

Split Mixed Line Endings

str.splitlines() recognizes carriage return, line feed, and CRLF without leaving the terminator in each returned line. Pass keepends=True when the original ending must remain attached to each item.

sample = "one\rtwo\r\nthree\n"

print(sample.splitlines())
print(sample.splitlines(keepends=True))
Python Pool infographic testing empty lines, Unicode, files, and round trips
Text checks: Empty lines, Unicode, files, and round trips.

Normalize In The Correct Order

When an application needs one in-memory newline convention, replace CRLF before replacing lone CR. Otherwise the two characters in a Windows ending become two LF characters.

def normalize_newlines(text):
    return text.replace("\r\n", "\n").replace("\r", "\n")

print(repr(normalize_newlines("a\r\nb\rc")))

Use newline=” For CSV

The csv module expects the file to be opened with newline=” so it can interpret record endings itself. This avoids platform-specific blank rows and preserves the module’s documented newline behavior.

import csv
from io import StringIO

text = "name,score\r\nAda,10\r\n"
rows = list(csv.reader(StringIO(text, newline="")))
print(rows)

The official Python splitlines documentation lists CR, LF, and CRLF boundaries, while the csv module documentation covers newline handling. For related cleanup, see removing newlines from a list and repr().

For related newline diagnostics and cleanup, compare removing newlines from a list, repr(), and Python trim methods when preserving or normalizing text boundaries.

Frequently Asked Questions

What does carriage return mean in Python?

The carriage return character is represented by \r; in terminal output it returns the cursor to the start of a line, while in text it can mark a line boundary.

How do I split text with carriage returns?

Use str.splitlines(), which recognizes carriage return, line feed, and CRLF boundaries and can optionally preserve the endings.

How do I normalize CRLF and CR newlines?

Replace CRLF with LF first, then replace remaining CR with LF so Windows and legacy carriage-return endings become one consistent in-memory form.

Why do CSV files use newline=”?

Opening CSV files with newline=” lets the csv module handle record endings itself and avoids platform-specific blank-row or newline surprises.

Subscribe
Notify of
guest
3 Comments
Oldest
Newest Most Voted
Josef
Josef
4 years ago

Kudos for that, that was Super!
Keep them coming, Bless you x

Dipka
Dipka
3 years ago

string = ‘My web\nsite is Latracal \rSolution’

print(string)

The output:
My web
SolutionLatracal

I don’t understand this.
The o/p i run on the compiler came out differentlyi.e,
My web
site is Latracal
Solution
> o/p i thought is
My web
Solutionctracal

Last edited 3 years ago by Dipka
Pratik Kinage
Admin
3 years ago
Reply to  Dipka

\r does not stand for a new line. \r\n does.