Remove Blank Lines in Python
If you're processing text in Python - parsing a log file, cleaning scraped data, prepping input for another script - blank lines are usually noise you want gone before the real work starts. Here's the standard way to do it, from a quick one-liner to a version that preserves paragraph spacing.
The one-liner
[line for line in text.splitlines() if line.strip()]This splits your text into a list of lines, then keeps only the ones where line.strip() is truthy. strip() removes leading and trailing whitespace from a line - so a line that's entirely whitespace (spaces, tabs) becomes an empty string after stripping, which Python treats as falsy, and the line gets filtered out along with the genuinely empty ones.
That's worth pausing on, because it's the same distinction covered inour regex guide: do you want to remove only lines with zero characters, or also lines that arewhitespace-only? The comprehension above treats both as "blank" and removes both. If you only want to catch truly empty lines and leave whitespace-only ones alone, check equality against an empty string directly instead: if line != '' (or if line for short, since Python treats '' as falsy too) - that skips thestrip() call entirely, so a line with just a space survives.
Reading and writing a file
The comprehension above works on a list of lines you already have in memory. Here's the full pattern for reading a file, filtering it, and writing the result back out:
with open('input.txt', 'r') as infile:
lines = infile.readlines()
cleaned = [line for line in lines if line.strip()]
with open('output.txt', 'w') as outfile:
outfile.writelines(cleaned)Two details that trip people up here: readlines() keeps the trailing\n on each line (unlike splitlines(), which strips it), so line.strip() in the filter is still doing the right thing - it's just checking for content, not removing the newline for you. And writelines() doesn't add newlines between items the way print() would, so if your lines don't already end in \n (say, because you built the list some other way), you'll need outfile.write('\n'.join(cleaned)) instead.
If you want to overwrite the same file rather than write to a new one, open it once with'r+', read everything, seek back to the start with.seek(0), write the filtered content, then .truncate() to remove any leftover trailing content from the original - or more simply, just read the whole file first, close it, then reopen it in 'w' mode for writing. Trying to read and write the same file handle at once without careful seeking is a common source of corrupted output.
Preserving spacing instead of removing it entirely
Everything above removes every blank line. If you're cleaning up a document and want to keep single blank lines between paragraphs - just collapse runs of two or more down to one - you need something with a bit of memory of the previous line:
result = []
prev_blank = False
for line in lines:
is_blank = line.strip() == ''
if is_blank and prev_blank:
continue
result.append(line)
prev_blank = is_blankThis walks through line by line, tracking whether the previous line was blank. If the current line is also blank and the previous one was too, it's skipped - but the first blank line in any run is kept, so you end up with exactly one blank line between sections instead of zero or several.
The same idea can be written more compactly with itertools.groupby, which groups consecutive lines by whether they're blank:
from itertools import groupby
result = []
for is_blank, group in groupby(lines, key=lambda line: line.strip() == ''):
if is_blank:
result.append('')
else:
result.extend(group)Both versions do the same thing; groupby is more idiomatic if you're already comfortable with itertools, but the flag-based loop is easier to read if you're not.
The regex alternative
If you'd rather reach for a regular expression than a loop, Python's re module handles it in one line:
import re
result = re.sub(r'^\s*\n', '', text, flags=re.MULTILINE)This uses the same pattern style covered in theregex guide -^\s* matches a line that's empty or whitespace-only, and the trailing\n consumes its newline so the line disappears entirely. The one Python-specific detail: unlike some editors that default to multiline matching, Python'sre module requires you to pass flags=re.MULTILINE explicitly, or ^ and $ will only match the very start and end of the whole string instead of each line.
Skip writing code entirely
For a one-off cleanup - pasting in a chunk of text once rather than processing files as part of a script - Remove Empty Lines does this instantly in your browser, no Python environment required.
This guide is part of our full roundup offree tools for cleaning up messy text, covering duplicate lines, blank lines, extra spaces, and dirty CSV exports.
Frequently Asked Questions
What's the difference between line.strip() and line != '' in the filter?
line.strip() (used as a truthy check) removes both fully empty lines and whitespace-only lines, since stripped whitespace becomes an empty string either way. line != '' only removes lines with zero characters - a line containing just a space passes through untouched. Choose based on whether whitespace-only lines count as "blank" for your use case.
Does splitlines() or readlines() matter for this?
Yes. splitlines() (used on a string already in memory) strips the newline characters from each line automatically. readlines() (used when reading a file object) keeps the trailing \n on every line except possibly the last. Both work fine with the filtering logic above, since strip() handles either case - but if you're writing the result back out, remember readlines()-sourced lines already have their newlines, while splitlines()-sourced ones don't.
Can I do this without loading the whole file into memory?
Yes - for large files, iterate over the file object directly instead of calling readlines(): for line in infile: if line.strip(): outfile.write(line). This processes one line at a time rather than holding the entire file in memory at once.
Why would I use groupby instead of the flag-based loop?
They produce identical results; it's purely a style choice. groupby avoids manually tracking state across iterations, which some people find cleaner, but it also requires importing itertools and understanding how grouping-by-key works. The flag-based loop is more explicit about what's happening, which can make it easier to modify later if your blank-line rules get more complex.
Is the regex version faster than the list comprehension?
For most file sizes, the difference is negligible - both run in linear time relative to the input. The list comprehension is generally easier to read and modify (e.g., adding additional filter conditions), while the regex version is more compact if blank-line removal is the only thing you need and you're already comfortable with regex.