Try the Character Frequency

Finding Invisible Characters: Debugging Encoding With Counts

Zero-width spaces, byte order marks, smart quotes and mojibake are invisible on screen and obvious in a character count. Here's how to find and fix them.

September 3, 2026 8 min read
Share: Facebook WhatsApp LinkedIn Email
Finding Invisible Characters: Debugging Encoding With Counts

The Bug That Isn't in Your Code

Your import script has run cleanly for eight months. Today it fails on one row out of 40,000, throwing an encoding error on a customer name that looks entirely normal on screen.

Or the search function stops matching a product that's definitely in the database. Or a CSV that opens fine in one program produces gibberish in another. Or a comparison between two seemingly identical strings returns false.

These problems share a cause: characters that exist in your data but aren't visible when you look at it. And the fastest way to find them is to stop looking at the text and start counting the characters.

What a Frequency Count Actually Reveals

Run a character frequency analysis on a body of clean English text and you get a predictable profile. Space is most common, then e, t, a, o, i, n. Punctuation appears at low frequencies. Everything is in the ASCII range.

Run it on dirty data and anomalies stick out immediately, because they appear at frequencies and in positions that don't fit the pattern.

The Character Frequency tool gives you this directly:

  1. Paste your text into the input.
  2. Read the per-character counts.
  3. Scan for characters you didn't expect.
  4. Check the counts of characters you did expect against what they should be.

That last step catches things the first misses. If you're expecting 40,000 rows in a CSV, you should see roughly 40,000 newline characters and a comma count that's close to a multiple of your column count. A mismatch tells you something structural is wrong before you've parsed a single row.

The Usual Suspects

Smart quotes and typographic punctuation

Text pasted from a word processor or a CMS brings its punctuation with it:

Character Name ASCII equivalent
' ' Left/right single quotation mark '
" " Left/right double quotation mark "
En dash -
Em dash -
Horizontal ellipsis ...
Prime, double prime ' "

These break things constantly. A CSV quoted with typographic quotes won't parse. Code copied from a blog post with smart quotes won't compile. A string comparison between O'Brien with a typographic apostrophe and O'Brien with a straight one returns false, which is exactly the kind of failure that produces "the record is definitely there but search can't find it."

In a frequency count, they show up as unexpected non-ASCII entries. Their presence alongside a lower-than-expected count of straight quotes is the giveaway.

Non-breaking spaces

U+00A0, the non-breaking space, looks exactly like a regular space and is not one.

It arrives from HTML ( ), from word processors, from PDF extraction, and from copy-pasting out of web pages. It survives a trim() in many implementations because the default whitespace definition doesn't always include it. It breaks split(' '). It makes "Total: 100" fail to match "Total: 100".

A frequency count showing a non-ASCII character at high frequency in a mostly-English document is almost always this.

Zero-width characters

These are the worst, because they're genuinely invisible:

  • U+200B zero-width space
  • U+200C zero-width non-joiner
  • U+200D zero-width joiner
  • U+FEFF zero-width no-break space, better known as the byte order mark

They come from HTML processors, from certain PDF exports, from text run through translation tools, and from deliberate insertion for word-wrap control.

A zero-width character in the middle of an email address, a product code, or an API key produces a value that looks correct and behaves incorrectly. You cannot see it. You cannot select it separately. A frequency count is one of the few ways to find it without writing a regex.

The byte order mark

U+FEFF at the start of a UTF-8 file deserves its own mention because it causes such a specific and confusing failure.

Import a CSV with a BOM and your first column header becomes \ufeffCustomerID instead of CustomerID. Every subsequent lookup by column name fails. The header prints normally in most terminals. It looks exactly right.

If a frequency count shows exactly one instance of a strange character and your first field lookup is failing, this is it. Save the file as "UTF-8 without BOM" and the problem disappears.

Mojibake

When UTF-8 bytes get interpreted as a single-byte encoding like Latin-1, you get characteristic garbage:

café       →  café
naïve      →  naïve
–          →  â€"
'          →  ’
"          →  “

The pattern is recognisable once you've seen it: Ã, Â, and sequences beginning †appear at frequencies far above anything natural.

A high count of  is a particularly reliable signal, because it's the mojibake form of a non-breaking space — meaning you have two encoding problems layered on each other.

Tabs, carriage returns and mixed line endings

Tabs hiding inside CSV fields. Carriage returns from Windows line endings (\r\n) that a Unix-oriented parser leaves attached to the end of the last field on each line. Files with a mixture of both because they were edited on two platforms.

A frequency count showing a \r count that differs from the \n count tells you immediately that your line endings are inconsistent.

Homoglyphs

Characters from other scripts that look identical to Latin letters. Cyrillic а (U+0430) versus Latin a (U+0061). Greek ο versus Latin o.

These are used deliberately in phishing domains and occasionally arrive accidentally through copy-paste from multilingual sources. They're invisible to the eye and obvious in a character count.

A Practical Workflow

Before importing any external data:

  1. Take a sample — a few thousand characters is plenty.
  2. Run a frequency count.
  3. List every character outside your expected set.
  4. Decide for each: normalise it, strip it, or accept it deliberately.

When a specific record misbehaves:

  1. Extract just that field's value.
  2. Run a frequency count on it alone.
  3. Compare against a known-good value of the same shape.

Comparing counts between a working and a failing record isolates the difference in seconds, where staring at the two strings can take an hour and still miss it.

When a file parses to the wrong number of rows or columns:

Count delimiters and newlines. If a 40,000-row, 6-column CSV shows 241,000 commas rather than 240,000, you have a stray delimiter inside a field somewhere.

Cleaning Rules Worth Standardising

Once you know what's in your data, normalisation is straightforward. A reasonable default pipeline for user-supplied or externally sourced text:

  • Strip the BOM if present at position zero
  • Replace non-breaking spaces with regular spaces
  • Remove zero-width characters entirely
  • Normalise typographic quotes and dashes to ASCII equivalents, unless you're preserving them deliberately for display
  • Normalise line endings to a single convention
  • Apply Unicode normalisation (NFC is the usual choice) so that composed and decomposed forms of accented characters compare equal
  • Trim whitespace using a definition that includes Unicode whitespace, not just ASCII

Apply this at the boundary — the moment data enters your system — rather than scattering fixes through your application. One normalisation function at the ingestion point prevents an entire category of bugs downstream.

Common Mistakes

Trusting your eyes. Almost none of these characters are visible. Visual inspection cannot find them.

Fixing symptoms individually. Patching the one record that failed leaves the other 300 waiting. Find the pattern, fix at the source.

Stripping all non-ASCII. Tempting and destructive. It mangles legitimate names, addresses and international content. Target specific problem characters instead.

Normalising display text and stored text differently. If you store one form and search against another, matching fails.

Assuming your editor shows you everything. Most text editors render zero-width characters as nothing at all. Some have a "show invisibles" mode; many don't cover the full set.

FAQ

How do I find a zero-width character in a large file? Run a frequency count on a sample to confirm which one is present, then search for its specific code point with a regex or a hex-aware editor.

What's the difference between a BOM and a zero-width no-break space? Same code point, U+FEFF. At the start of a file it functions as a byte order mark; elsewhere it's a zero-width no-break space. Context determines the interpretation.

Should I convert smart quotes to straight quotes? For data fields, identifiers and anything used in comparisons, yes. For prose intended for display, typographic quotes are usually the better typography — the key is being consistent about which contexts get which.

Why does my string comparison fail on visually identical text? Most often an invisible character, a non-breaking space, or two different Unicode normalisation forms of the same accented character. A frequency count distinguishes all three.

Is mojibake reversible? Often yes, if you can identify the exact encoding pair involved and the data hasn't been double-mangled. Prevention is much easier than recovery.

The Takeaway

Encoding bugs are hard because the evidence is invisible in the medium you're inspecting. Switching from reading text to counting characters turns an invisible problem into an obvious one, usually in under a minute.

Analyse any text with the free Character Frequency tool at sadiqbd.com — no sign-up, instant results.

Ask AI about this article
Share: Facebook WhatsApp LinkedIn Email

Character Frequency

Free, instant results — no sign-up required.

Open Character Frequency →
Similar Tools
Text Diff Remove Duplicate Lines Morse Code Translator Sort Lines Text Reverser Lorem Ipsum Generator Text to Slug Word & Character Counter
Frequency Analysis: How Counting Letters Breaks Caesar Ciphers, Substitution Ciphers, and Why Modern Encryption Is Immune
Text Tools
Frequency Analysis: How Counting Letters Breaks Caesar Ciphers, Substitution Ciphers, and Why Modern Encryption Is Immune
Huffman Coding: How "E Is the Most Common Letter" Becomes Smaller ZIP Files and PNG Images
Text Tools
Huffman Coding: How "E Is the Most Common Letter" Becomes Smaller ZIP Files and PNG Images
How Counting Characters Can Identify a Language — and Why It Gets More Reliable With Every Word
Text Tools
How Counting Characters Can Identify a Language — and Why It Gets More Reliable With Every Word
How Character Frequency Reveals Who Wrote a Text — Stylometry, Forensic Linguistics, and What It Detects in AI Writing
Text Tools
How Character Frequency Reveals Who Wrote a Text — Stylometry, Forensic Linguistics, and What It Detects in AI Writing