UUID Studio

Text Diff

Line-by-line diff for plain text, configs, and logs.

  • 🔒 No data stored or uploaded
  • âš¡ 100% client-side
  • 🆓 Free, no account

Need more than one tool at a time? Open the full Workbench - or press Ctrl+K to jump to any tool.

Create or edit JSON here (syntax colors), then format, validate, or convert - same as MongoDB $binary UUID blobs on the Convert tab once detected.

New document

About Text Diff

A text diff finds the smallest set of insertions and deletions that turns one text into another. Nearly every implementation is a variant of the Myers algorithm, which frames the problem as finding the longest common subsequence - that framing is why a diff sometimes attributes a change in a way that is technically minimal but not what a human would say happened.

Granularity is the choice that matters most. Line-level diffing is what git does and is right for code, where a line is a meaningful unit. Word-level is better for prose, where a single changed word would otherwise mark the whole paragraph. Character-level is the finest and is what you want when hunting a one-character difference in a token, a hash or an identifier.

That last case is where a diff tool earns its keep, because the differences are often invisible. Trailing whitespace, a tab where spaces were expected, CRLF against LF line endings, a non-breaking space pasted from a web page, a Unicode minus sign instead of a hyphen, or a zero-width character from a word processor - all of these produce two strings that look identical and compare unequal.

Line endings deserve particular attention because they cause diffs that appear to change every line. A file edited on Windows and committed without normalisation shows as entirely rewritten, which is what .gitattributes and core.autocrlf exist to prevent.

Comparison runs in your browser, so two real files can be diffed without either being uploaded.

How to use the Text Diff

  1. Paste the two texts. Order matters only for how additions and removals are labelled.
  2. Pick the granularity: lines for code, words for prose, characters for hunting a single wrong character.
  3. If the texts look identical but differ, enable whitespace visibility - that is where the difference almost always is.
  4. For JSON or structured data, use a structural comparison instead; a text diff will report key reordering as a change.

Examples

  • Text A
    line one
    line two
    line three
  • Text B
    line one
    line TWO
    line three
    line four

Text Diff in code

The same operation this tool performs, in the languages you are most likely to need it.

Command line - finding invisible differences
# Show non-printing characters: $ for line ends, ^I for tabs
diff <(cat -A a.txt) <(cat -A b.txt)

# Ignore whitespace entirely
diff -w a.txt b.txt

# Byte-level view - the last resort, and the one that always works
cmp -l a.txt b.txt
diff <(xxd a.txt) <(xxd b.txt)

# Detect CRLF line endings
file a.txt                  # "with CRLF line terminators"
grep -c $'\r' a.txt         # count of CR characters

# git's word-level diff, for prose
git diff --word-diff=color
JavaScript - normalise before comparing
// Unicode normalisation: "é" can be one code point or two
// (e + combining accent). They look identical and compare unequal.
const norm = (s) => s.normalize("NFC");
norm("café") === norm("cafe\u0301");   // true

// Strip the characters that cause invisible mismatches
const clean = (s) =>
  s
    .replace(/\r\n/g, "\n")        // CRLF -> LF
    .replace(/\u00a0/g, " ")       // non-breaking space
    .replace(/[\u200b-\u200d\ufeff]/g, "")  // zero-width + BOM
    .replace(/[ \t]+$/gm, "");     // trailing whitespace

// Find the first differing character position
function firstDiff(a, b) {
  const n = Math.min(a.length, b.length);
  for (let i = 0; i < n; i++) {
    if (a[i] !== b[i]) {
      return { at: i, a: a.codePointAt(i).toString(16), b: b.codePointAt(i).toString(16) };
    }
  }
  return a.length === b.length ? null : { at: n, reason: "length" };
}
Python
import difflib, unicodedata

a = open("a.txt").read().splitlines()
b = open("b.txt").read().splitlines()

print("\n".join(difflib.unified_diff(a, b, "a.txt", "b.txt", lineterm="")))

# How similar are two strings, 0.0-1.0?
difflib.SequenceMatcher(None, "colour", "color").ratio()

# Normalise Unicode before comparing - NFC composes accents
unicodedata.normalize("NFC", s1) == unicodedata.normalize("NFC", s2)

# Reveal the invisible: show every character's code point
print([hex(ord(c)) for c in line])

When you need this

  • Finding the one character that differs between two tokens or hashes.
  • Comparing configuration between two environments.
  • Reviewing what changed between two versions of a document or query.
  • Diagnosing why two apparently identical strings fail an equality check.
  • Checking whether a file was rewritten with different line endings.

Common problems and what causes them

Every line showing as changed
Almost always line endings: one file uses CRLF and the other LF, so every line differs by an invisible character. Check with `file` or `grep -c $'\r'`, and configure .gitattributes so it stops happening.
Strings that look identical but compare unequal
Trailing whitespace, a non-breaking space (U+00A0) pasted from a web page, a Unicode minus or en dash instead of a hyphen, a zero-width space, or a byte-order mark. Enable whitespace visibility, or dump code points for the region.
Unicode composition differences
é can be a single code point (U+00E9) or e followed by a combining accent (U+0065 U+0301). They render identically and are different strings. Normalise to NFC before comparing.
Text-diffing JSON or XML
A text diff reports key reordering and reformatting as changes even though the data is identical. Use a structural comparison, or normalise first - jq -S for JSON.
Minified or single-line files
A line diff on a file with one enormous line tells you only that the line changed. Switch to character or word granularity, or format both sides first.
A minimal diff that is not the intuitive one
Diff algorithms optimise for the fewest edits, which can attribute a moved block to the wrong side or split a rename oddly. git's --patience and --histogram algorithms often produce more readable output for code.

FAQ

Why does my diff show every line as changed?
Line endings. One file has CRLF and the other LF, so every line differs by an invisible carriage return. Confirm with `file a.txt`, normalise the file, and set .gitattributes to prevent a recurrence.
Two strings look identical but are not equal - what is going on?
Look for trailing whitespace, a non-breaking space from a copy-paste, a Unicode dash instead of a hyphen, a zero-width character, a BOM, or a decomposed accent. Dumping code points for the region settles it immediately.
Which granularity should I use?
Lines for code, because a line is the meaningful unit. Words for prose, so a single changed word does not flag a whole paragraph. Characters when you are hunting one wrong character in a token, hash or identifier.
Can I diff JSON with a text diff?
You can, but it will report key reordering and whitespace as differences. Normalise both sides first (jq -S) or use a structural JSON comparison, which understands that object key order is not significant.
Are my files uploaded?
No. The comparison runs entirely in your browser, so two real configuration files can be diffed without either leaving the tab.
Does this diff word-by-word or character-by-character?
No, it compares whole lines. A single changed character marks the entire line as different - use it to spot which lines changed, then look within the line for the specific edit.
Should I use this or JSON Compare for JSON files?
Use JSON Compare for JSON - it understands structure and key semantics. This tool is for everything that isn't JSON, or when you specifically want a literal line-by-line view.

Related reading