Remove Newlines From a String in Python (and the \r Trap)

To remove a newline from a string in Python, there are two different jobs, and picking the wrong one is why newlines keep coming back:

text.strip()               # remove newlines from the ENDS only
text.replace("\n", "")     # remove EVERY newline
text.replace("\n", " ")    # replace each one with a space
" ".join(text.splitlines()) # the version that survives Windows files

If your text came from a file, a web page or a Windows machine, only that last line is reliable. The reason is \r\n, and it is covered below.

Every result on this page is a real run on Python 3.12.5.

Strip from the ends or remove them all?

Decide which one you need before you pick a method, because they don’t overlap:

text = "Hello\nWorld\n"

print("original      :", repr(text))
print()
print("strip()       :", repr(text.strip()))       # only the ends
print("rstrip()      :", repr(text.rstrip()))      # only the end
print('replace("")   :', repr(text.replace("\n", "")))    # everywhere
print('replace(" ")  :', repr(text.replace("\n", " ")))   # everywhere, keeping a gap

Output:

original      : 'Hello\nWorld\n'

strip()       : 'Hello\nWorld'
rstrip()      : 'Hello\nWorld'
replace("")   : 'HelloWorld'
replace(" ")  : 'Hello World '
Command Prompt comparing Python strip rstrip and replace for removing newlines from a string
strip touches only the ends. replace touches everything.
GoalUse
Trailing newline from a read lineline.rstrip("\n")
Whitespace and newlines from both endstext.strip()
Every newline gonetext.replace("\n", "")
Every newline becomes a spacetext.replace("\n", " ")
Reliable across platforms" ".join(text.splitlines())

Replacing with an empty string joins words together. "Hello\nWorld" becomes "HelloWorld", which is almost never what you meant.

Using strip to remove a trailing newline

strip removes whitespace from both ends, and a newline counts as whitespace, so it’s often all you need:

line = "  Hello World  \n"

print("original        :", repr(line))
print("strip()         :", repr(line.strip()))
print("rstrip()        :", repr(line.rstrip()))
print("lstrip()        :", repr(line.lstrip()))

print()
# passing characters restricts what gets removed
print('rstrip("\\n")    :', repr(line.rstrip("\n")), "  spaces kept")

print()
# the argument is a SET of characters, not a suffix
print('"banana".rstrip("na") ->', repr("banana".rstrip("na")), "  not 'bana'")

Output:

original        : '  Hello World  \n'
strip()         : 'Hello World'
rstrip()        : '  Hello World'
lstrip()        : 'Hello World  \n'

rstrip("\n")    : '  Hello World  '   spaces kept

"banana".rstrip("na") -> 'b'   not 'bana'

Passing an argument narrows it, but watch the last line of that output. rstrip("na") strips any n and any a, not the string "na".

That catches people out constantly. The argument is a set of characters to remove, not a suffix to match.

For removing an actual suffix, Python 3.9 added removesuffix, which does exactly what people expect rstrip to do.

Why does replace leave a carriage return in the string?

This is the bug that wastes afternoons. Windows files end every line with \r\n, two characters:

# a file written on Windows uses \r\n for every line break
windows_text = "Hello\r\nWorld\r\n"

print("original          :", repr(windows_text))

broken = windows_text.replace("\n", "")
print('replace("\\n", "")  :', repr(broken), "  <- carriage returns survived")

print()
print("length now        :", len(broken), "but 'HelloWorld' is", len("HelloWorld"))
print('equals "HelloWorld"?', broken == "HelloWorld")

print()
print("the stray characters are invisible when printed, which is why")
print("string comparisons start failing for no apparent reason")

Output:

original          : 'Hello\r\nWorld\r\n'
replace("\n", "")  : 'Hello\rWorld\r'   <- carriage returns survived

length now        : 12 but 'HelloWorld' is 10
equals "HelloWorld"? False

the stray characters are invisible when printed, which is why
string comparisons start failing for no apparent reason
Command Prompt showing that replacing only the newline character leaves carriage returns in a Windows string
Length 12 instead of 10, and the equality check fails.

Removing \n leaves the \r in place. It prints as nothing visible, so the string looks right and compares as wrong.

The symptom is a comparison or a dictionary lookup that fails on data which appears identical on screen. Always check with repr() before assuming your eyes.

You can chain two replaces, but there is a better answer in the next section. The same invisible-character problem shows up when you remove the last character from a string.

Using Python splitlines to handle every line ending

splitlines() knows about all of them, so you never have to think about which platform produced your text:

samples = {
    "unix":    "Hello\nWorld",
    "windows": "Hello\r\nWorld",
    "old mac": "Hello\rWorld",
    "mixed":   "One\nTwo\r\nThree\rFour",
}

for name, text in samples.items():
    joined = " ".join(text.splitlines())
    print(f"{name:<8} {repr(text):<28} -> {repr(joined)}")

print()
print("splitlines() understands every line ending, so this works everywhere")

Output:

unix     'Hello\nWorld'               -> 'Hello World'
windows  'Hello\r\nWorld'             -> 'Hello World'
old mac  'Hello\rWorld'               -> 'Hello World'
mixed    'One\nTwo\r\nThree\rFour'    -> 'One Two Three Four'

splitlines() understands every line ending, so this works everywhere
Command Prompt showing Python splitlines correctly handling Unix Windows and old Mac line endings
Four different line endings, one correct result each time.

Split on the line breaks, then join with whatever separator you want. A space for prose, an empty string to concatenate, a comma for a list.

This is the version to reach for by default. It costs nothing extra and removes a whole class of platform bugs.

Removing blank lines and extra whitespace from a string

Real text has runs of blank lines and stray tabs. Replacing newlines one for one leaves all of that behind:

import re

messy = "Hello\n\n\n   World\t\tagain\n"

print("original        :", repr(messy))
print()
print('replace only    :', repr(messy.replace("\n", " ")))
print("splitlines/join :", repr(" ".join(messy.split())))
print("regex collapse  :", repr(re.sub(r"\s+", " ", messy).strip()))

print()
print("split() with no argument splits on ANY run of whitespace")
print("and discards the empties, which is usually exactly what you want")

Output:

original        : 'Hello\n\n\n   World\t\tagain\n'

replace only    : 'Hello      World\t\tagain '
splitlines/join : 'Hello World again'
regex collapse  : 'Hello World again'

split() with no argument splits on ANY run of whitespace
and discards the empties, which is usually exactly what you want

" ".join(text.split()) is the shortest tidy-up in Python. Calling split() with no argument splits on any run of whitespace and drops the empty pieces.

re.sub(r"\s+", " ", text) does the same thing and is worth using when you already have re imported or need a different replacement.

Removing newlines when reading a file

readlines() keeps the newline on the end of every line, which is where most of these questions start:

# readlines keeps the newline on the end of every line
raw_lines = ["first\n", "second\n", "third\n"]

print("as read        :", raw_lines)
print()
print("stripped       :", [line.strip() for line in raw_lines])
print("rstrip newline :", [line.rstrip("\n") for line in raw_lines])

print()
# the tidy way to read a file without trailing newlines
text = "first\nsecond\nthird\n"
print("splitlines()   :", text.splitlines(), "  no trailing newlines at all")

print()
print("note splitlines() also drops the empty final entry that split('\\n') leaves:")
print("split      :", text.split("\n"))
print("splitlines :", text.splitlines())

Output:

as read        : ['first\n', 'second\n', 'third\n']

stripped       : ['first', 'second', 'third']
rstrip newline : ['first', 'second', 'third']

splitlines()   : ['first', 'second', 'third']   no trailing newlines at all

note splitlines() also drops the empty final entry that split('\n') leaves:
split      : ['first', 'second', 'third', '']
splitlines : ['first', 'second', 'third']
Command Prompt showing how to strip trailing newlines from lines read from a file in Python
Note the empty string that split leaves and splitlines does not.

Look at the final comparison. text.split("\n") produces an empty final entry because the text ends with a newline. splitlines() does not.

That phantom empty string is a common source of a stray blank row at the end of processed data.

Iterating the file object directly gives you the same lines with the same trailing newlines, so line.rstrip("\n") inside the loop is the usual fix. It pairs well with splitting strings.

Common newline removal mistakes

SymptomCauseFix
Words run togetherreplace("\n", "")Replace with a space
Comparison fails on identical textStray \r left behindUse splitlines()
Only the ends were cleanedUsed stripUse replace or splitlines
Too many characters removedrstrip("abc") strips a character setUse removesuffix
Blank entry at the end of a listsplit("\n") on trailing newlineUse splitlines()

Other Python string cleaning guides:

Frequently asked questions

How do I remove newlines from a string in Python?

text.replace("\n", " ") removes them all, and text.strip() removes only the ones at the ends. String methods are listed in the Python string methods reference.

What is the difference between strip and replace for newlines?

strip only affects the beginning and end of the string. replace affects every occurrence, including newlines in the middle.

How do I remove \n from the end of a line?

line.rstrip("\n"), or line.strip() if trailing spaces should go too.

Why is there still a character left after removing \n?

The text uses Windows line endings, \r\n. Removing \n leaves the carriage return behind, where it is invisible but still counted.

What is the safest way to remove newlines across platforms?

" ".join(text.splitlines()). splitlines() understands \n, \r\n and a lone \r.

How do I remove blank lines as well as newlines?

" ".join(text.split()) collapses every run of whitespace, including blank lines and tabs.

Why does rstrip remove too many characters?

The argument is a set of characters, not a suffix. "banana".rstrip("na") returns "b". Use removesuffix for a literal ending.