Invisible characters: what they are and how to get rid of them
Your text looks fine and behaves badly. These are the usual six culprits.
When a search finds nothing, two identical rows refuse to deduplicate, or a CSV import fails on the first column, the cause is usually an invisible character. The common ones are the non-breaking space (U+00A0), the zero-width space (U+200B), the byte order mark (U+FEFF) and the soft hyphen (U+00AD). They arrive by copy-paste and are impossible to see.
The six you will actually meet
| Character | Code | Where it comes from | What it breaks |
|---|---|---|---|
| Non-breaking space | U+00A0 | web pages, Word, PDF copy, | search, split by space, trim, number parsing |
| Zero-width space | U+200B | CMS editors, line-break hints, some generators | exact match, deduplication, word count |
| Zero-width joiner | U+200D | emoji sequences, fancy text generators | character count, string slicing, reversal |
| Byte order mark | U+FEFF | files saved as UTF-8 with BOM in Notepad or Excel | the first CSV header, JSON parsing, shell scripts |
| Soft hyphen | U+00AD | PDF and Word hyphenation | search inside words, spellcheck |
| Right-to-left mark | U+200F | pasted Arabic or Hebrew text | display order of surrounding text |
The BOM deserves special mention because it produces the single most confusing error message in data work: a CSV where the first column header is id and nothing matches id. In Excel, save as CSV UTF-8 and you get a BOM; save as CSV (Comma delimited) and you get the system code page instead. Neither is plain UTF-8.
How to see them
- Word: press Ctrl+Shift+8 or click the pilcrow button. A non-breaking space shows as a raised degree-like circle instead of a middle dot. Soft hyphens appear as a small hyphen with a hook.
- Notepad++: View → Show Symbol → Show All Characters. Non-breaking spaces are highlighted in orange in recent versions.
- VS Code: highlights unusual invisible characters by default since 2021 and offers to remove them. The setting is
editor.unicodeHighlight.invisibleCharacters. - Any browser: select the suspicious text and count. If the character count is higher than the letters you can see, something is hiding — a character counter that reports with and without spaces makes this a two-second check.
- Command line:
cat -A file.txton Linux, orhexdump -C file.txt | head. A BOM shows asef bb bfat the very start.
Removing them without damaging the text
Word. Find and Replace, then in the Find field type ^s for a non-breaking space and ^- for an optional hyphen, replacing with a normal space and nothing respectively. Turn off AutoFormat as you type afterwards or Word puts some of them back.
Notepad++. Search mode Extended, find \xA0, replace with a space. For zero-width characters use Regular expression mode and [\x{200B}\x{200C}\x{200D}\x{FEFF}], replace with nothing.
Google Sheets. =CLEAN() only strips the first 32 control characters and misses U+00A0 entirely. Use =SUBSTITUTE(A2,CHAR(160)," ") and then =TRIM(). This is why TRIM alone so often appears not to work.
Excel. Same story: =SUBSTITUTE(A2,CHAR(160)," ") before =TRIM(). Find and Replace also works if you paste an actual non-breaking space into the Find box, which is awkward but reliable.
Python or a script. s.replace(chr(160),' ') for the space, and s.encode().decode('utf-8-sig') to drop a BOM while reading a file. In a shell, sed -i '1s/^\xEF\xBB\xBF//' file.csv removes the BOM from the first line only, which is the safe version — a global substitution would also delete legitimate U+FEFF characters further into the file.
Browser. Paste into a text cleaner that normalises whitespace and strips zero-width characters, then copy back. This is the fastest route when the text came from a PDF or a web page and you do not know what is in it.
What usually goes wrong
- TRIM does nothing. TRIM only removes the ordinary space, U+0020. Substitute CHAR(160) first, always.
- Two identical rows will not deduplicate. One has a trailing non-breaking space. Clean before you remove duplicates, not after.
- Numbers import as text. A thousands separator that is a non-breaking space — standard in French and Russian formatting — makes
1 200unparseable. Replace it before importing. - Stripping every zero-width character breaks emoji. Family and profession emoji are built from zero-width joiners. Removing U+200D turns one emoji into three. If the text contains emoji, target U+200B and U+FEFF only.
- Removing the BOM breaks a file that needed it. Some older Windows tools require it to detect UTF-8. Change the consumer, not the file, when you have that choice.
- Word count disagrees with the platform. Zero-width characters inflate character counts without changing what you see. Recount with a word counter after cleaning.
- Sorting puts an entry in the wrong place. A leading invisible character sorts before every letter. Clean, then sort.
A standing rule for pasted text
Anything arriving from a PDF, a web page, a Word document or a chat message gets cleaned before it enters a spreadsheet, a database or a CMS. Three steps, in order: replace non-breaking spaces with ordinary spaces, strip zero-width characters, trim each line and collapse runs of whitespace.
Then count. If the character count still exceeds what you can see by more than the spaces you counted, something else is in there — usually a control character from a database export. At that point look at the bytes rather than guessing.
If you own the pipeline, do it once at the boundary instead of in every sheet: normalise input to NFC, map U+00A0 to a plain space, drop U+200B and U+FEFF, and reject anything still containing control characters below U+0020 other than tab and newline. Four rules, applied at the point data enters the system, remove the problem for everyone downstream.
The habit costs about fifteen seconds and removes an entire category of bug that is otherwise nearly impossible to diagnose, because the evidence is invisible by definition.
Related tools
Frequently asked questions
The character U+00A0. It looks like a space but prevents a line break at that point. It comes from web pages and Word, and it breaks search, TRIM and number parsing.
TRIM only handles the ordinary space U+0020. Replace CHAR(160) with a normal space first, then apply TRIM.
The byte order mark U+FEFF, added when a file is saved as UTF-8 with BOM. It attaches to the first column header and stops it matching anything.
Ctrl+Shift+8 in Word, Show All Characters in Notepad++, or compare the character count against the letters you can actually see.
Not if the text contains emoji. Zero-width joiners hold multi-part emoji together, so target the zero-width space and the BOM instead.
CMS editors inserting line-break hints, some fancy text generators, and copy-paste from sites that use them to control wrapping.