Whitespace problems, and where they come from
Why a string that looks clean still fails a comparison, what a non-breaking space is doing in your text, and the invisible characters worth stripping.
Last reviewed · 1,351 words
In short
- Trailing whitespace is invisible and breaks exact comparisons, form validation and deduplication. It is the most common cause of "these look identical but do not match".
- Copying from a web page or a PDF brings non-breaking spaces, which look like spaces and are a different character entirely.
- The double space after a full stop is a typewriter convention. Proportional fonts space sentences correctly on their own.
- Zero-width characters are genuinely invisible and can be pasted in without any trace on screen.
- Line endings differ between Windows and everything else, which is why a file can show as entirely changed in a diff.
Whitespace is the text you cannot see, and almost every problem it causes is a problem of invisibility — the characters are doing something and nothing on screen shows it.
The common problems
Trailing whitespace. Spaces at the end of a line or a field. Invisible, and it breaks exact string comparison, deduplication, and form validation. When two entries look identical and the system insists they differ, this is usually why.
Leading whitespace. Space before text, frequently introduced by copying from a formatted source.
Multiple spaces between words. From manual alignment, from deleting a word and leaving both spaces, or from a paste that collapsed a tab.
Multiple blank lines. Accumulated across edits.
Mixed tabs and spaces. Visually identical in many editors and a genuine error in Python, where indentation is syntax.
Spaces before punctuation. "word ." is wrong in English and correct in French, where the convention requires a thin space before high punctuation.
Non-breaking spaces
The non-breaking space, U+00A0, looks exactly like an ordinary space and behaves differently: it prevents a line break at that point.
It has legitimate uses — keeping "10 kg" or "Dr. Sharma" together across a line break — and it arrives uninvited whenever you copy from a web page, a PDF or a word processor.
The problem is that it is a different character. A search for "10 kg" with an ordinary space will not find "10 kg" written with a non-breaking one. Neither will a comparison, a lookup or a regular expression written the obvious way.
The same applies to a family of related characters that all render as blank space: the en space, em space, thin space, hair space, figure space and ideographic space. Any of them can appear in text copied from a typeset source, and none of them is the space you typed.
Zero-width characters
These are worse, because they occupy no visual space at all.
| Character | Purpose |
|---|---|
| Zero-width space, U+200B | Allows a line break without a visible space |
| Zero-width non-joiner, U+200C | Prevents ligature formation |
| Zero-width joiner, U+200D | Joins characters; builds emoji sequences |
| Word joiner, U+2060 | Prevents a line break |
| Byte order mark, U+FEFF | File encoding marker; frequently a stray at the start of a file |
They cause failures with no visible cause: a comparison that fails, a lookup that returns nothing, a number that will not parse. They are pasted in from web pages, from formatting tools, and occasionally deliberately as a watermark.
The zero-width joiner is not always noise — it is what makes multi-person emoji and skin-tone variants work — so stripping every zero-width character from arbitrary text can break emoji. A cleaner intended for prose should say which it removes.
Line endings
Three conventions, one of which is still in wide use for a reason that no longer exists.
| Convention | Characters | Systems |
|---|---|---|
| LF | \n | Unix, Linux, macOS, the web |
| CRLF | \r\n | Windows |
| CR | \r | Classic Mac OS, historical |
CRLF descends from mechanical teleprinters, where the carriage return moved the print head back and the line feed advanced the paper — two physical actions requiring two characters.
The practical consequence today: a file edited on Windows and committed to a repository can appear entirely rewritten in a diff, because every line ending changed. Git's core.autocrlf setting exists to manage this, and a .gitattributes file specifying text=auto is the more reliable fix.
Mixed line endings inside one file are worse than either convention consistently applied, and they are what happens when two people edit the same file on different systems without either setting configured.
The double space after a full stop
Two spaces after a sentence is a typewriter convention. Typewriters were monospaced, so every character occupied the same width and an extra space was needed to make sentence breaks visible.
Proportional fonts — which is everything you read on a screen or in print — space sentences correctly with one space. Every major style guide now specifies one, and HTML collapses consecutive spaces to one regardless of what you typed.
It is a habit rather than an error, and it is worth cleaning out of anything being published, where it produces uneven spacing in justified text.
Where cleaning matters
Data import. Trailing spaces in a CSV create duplicate categories: "Mumbai" and "Mumbai " become two distinct values in every group-by and every join.
Form input. Users paste email addresses and phone numbers with leading or trailing spaces constantly. Trimming input before validation removes a large share of "invalid email" complaints from users whose email is fine.
Database keys and lookups. An untrimmed key is a key that will not be found.
Code. Trailing whitespace produces noisy diffs, and most editors can strip it on save. Doing so once across an old codebase produces one enormous commit and quiet diffs thereafter.
Published text. Multiple spaces and stray blank lines break the rhythm of typeset text, and HTML collapses them in ways that look accidental where a table or a code block is involved.
What to strip, and what to keep
Cleaning is not universally safe, and the exceptions matter.
Keep indentation in code, YAML, Python and Markdown, where leading whitespace is meaningful.
Keep line breaks in poetry, addresses and anything where the shape is the content.
Keep non-breaking spaces where they were placed deliberately — between a number and its unit, in a name, after an abbreviation.
Keep the zero-width joiner in text containing emoji.
The general rule: collapse runs of spaces within a line, trim each line's ends, and be conservative about everything else. A cleaner that flattens all whitespace produces text that is uniform and occasionally wrong.
Finding the invisible
You cannot fix what you cannot see, and every good editor can make whitespace visible.
In a code editor, turn on "render whitespace" — spaces appear as dots, tabs as arrows. VS Code, Sublime and most others have it under view settings, and leaving it on permanently costs nothing once you are used to it.
In a word processor, the pilcrow button shows paragraph marks, spaces and tabs.
With a regular expression, a pattern matching whitespace at the end of a line finds trailing space on every line, and one anchored to the start finds leading space. Runs of two or more spaces are equally easy to target.
For the truly invisible, a hex view or a character inspector is the only reliable way. A zero-width character will not show up in any rendered view, and copying the suspect text into a tool that lists code points is the fastest way to identify it.
The diagnostic that catches most cases without any tooling: compare the lengths of two strings that look identical. If they differ, something invisible is in one of them.
Normalising before comparing
Where text will be compared, matched or deduplicated, normalise it first and store the normalised form alongside the original.
A reasonable normalisation for user-entered text: trim both ends, collapse internal runs of whitespace to a single space, convert Unicode space variants to ordinary spaces, and apply Unicode NFC normalisation so that accented characters composed two different ways compare equal.
That last one is a separate problem worth knowing about. An accented character can be a single code point or a base letter followed by a combining accent, and the two are visually identical and not equal as strings. macOS and Windows have historically preferred different forms, so a filename created on one can fail to match on the other.
Keep the original for display and use the normalised copy for matching. Overwriting the original with a cleaned version loses information that occasionally turns out to matter — deliberate spacing in a name, meaningful indentation, a line break the author intended.
What this tool assumes
- Runs of spaces and tabs within a line are collapsed to a single space.
- Leading and trailing whitespace is removed from each line and from the text as a whole.
- Multiple consecutive blank lines are reduced, with paragraph breaks preserved.
- Non-breaking and other Unicode space characters are normalised to ordinary spaces.
- Everything runs in your browser. Nothing you paste is sent anywhere.