Cat Toolbox
Help CatBlog About Partnership 中文

Comparing Two Versions of a File, and Testing a Regex Before You Ship It

DeveloperUpdated 2026-09-29

Two pages for the checking half of a job: Text Diff tells you what changed between two versions, and Regex Tester tells you what a pattern actually matches. Both are honest about small inputs and quietly misleading about certain large ones, so the useful thing to know is where each one's assumption sits.

Diff compares lines, and only lines

Paste the old version left, the new version right, press Compare. The tool finds the longest sequence of lines that appears in both, then treats everything else as added or deleted. The output is two columns, one row per step, with a blank placeholder on the side that has nothing at that position — so line numbers on each side stay independent and still line up vertically.

Four consequences of the line-only model:

  • There is no "modified" state. A line that changed by one character counts as one Deleted line and one Added line, and the summary reports exactly that. Nothing in the header tells you how many lines were edited rather than moved, so read the two panes across, not the counts.
  • Nothing inside a line is compared. title = "old" versus title = "new" shows as two whole-line changes. For a long line, the actual difference can be anywhere in it.
  • Order changes look like edits. Moving a function to the top of a file reports every line in between as added and deleted.
  • A replaced block can be presented either way. When two alignments score equally, the algorithm prefers the one that reads as an addition. The set of differences is the same; which side a particular line lands on can differ from another tool's output.

The one diff trap to check before you trust the result

The tool splits on \n and does not normalise line endings. A file saved with Windows CRLF endings pasted into the left box, and the same file with Unix LF endings in the right box, will differ on every single line, because the left-hand lines each carry an invisible trailing \r.

Symptom: two files that look identical produce an all-red, all-green diff with Added and Deleted counts equal to the total line count. That is not a content difference; it is line endings. Re-paste both sides from the same source, or run them through Trim Lines on the text formatter first.

One related detail: a single trailing empty line is discarded from each side before comparing. Two trailing newlines leave one blank line behind, which then genuinely participates in the diff.

Long lines break the two-column alignment

Rows size themselves to their content and wrap rather than scroll. Because the panes are two independent columns, a line that wraps on one side makes that row taller than its counterpart, and every row below it is now offset — the left pane and the right pane no longer visually correspond.

Nothing warns you this has happened. If the columns look shifted, scroll to the first long line and read from there; keeping lines short is what preserves the correspondence.

Diff cost grows as the product of the two sizes

The comparison builds a full table covering every line on one side against every line on the other. In the browser, 3,000 lines against 3,000 took about a fifth of a second. Doubling both sides quadruples the work and the memory — a 6,000 × 6,000 pair is 36 million cells.

There is no size limit, no background worker and no cancellation. Past a certain input the tab stops responding for a long time rather than showing an error. There is also no re-run when you edit: the result on screen is from the last time you pressed Compare, so if you typed after that, you are reading a stale diff.

Use this tool for files in the low thousands of lines and for change review. For very large files, a command-line diff that works in a streaming fashion is the right instrument.

Regex: everything updates as you type, including the mistakes

The tester re-runs the match on each keystroke in either the pattern or the test text. That is what makes it pleasant to explore, and it is also the mechanism by which a bad pattern takes the tab with it.

Patterns with nested quantifiers — (a+)+b, (\w*\s*)* — can require the engine to explore an exponential number of ways to match before concluding there is no match. There is no timeout, so an input like that does not return an error; it stops the page. Recovery is to close or reload the tab. Bound your repetitions ({0,10}) when you are probing unfamiliar input, and if you must test an unbounded pattern, paste a short test string first.

What each of the six flags changes here

  • g (on by default) — find every match instead of only the first. Without it, both the highlighted output and the statistics show a single match, and Replace changes only the first occurrence.
  • i — case-insensitive.
  • m — makes ^ and $ anchor to each line instead of the whole text. This one matters: the ten reference patterns below the tester are inserted without touching your flags, so ^[\u4e00-\u9fa5]+$ ("Chinese Characters Only") will not match the Chinese line inside a multi-line sample until you tick m.
  • s — lets . match newline characters.
  • u — Unicode mode; stricter about escapes, treats surrogate pairs as one character.
  • y — sticky: a match must begin exactly where the engine's cursor already is. Combined with g, the loop advances that cursor only on a hit, so a pattern that does not match at position 0 reports no matches at all. That combination is rarely what you want.

An invalid pattern is not dressed up: the raw engine message appears with a warning icon, in whatever language your browser reports. Same syntax errors will read differently in Chrome, Firefox and Safari.

Reading the statistics line

The status under the box reports three things: how many matches, the total number of characters matched, and — only if the first match contains capture groups — the group count.

Two things to know about those numbers:

  1. The group count comes from the first match only. A pattern whose later matches would produce a different number of groups is not reflected; nothing here shows you what each group actually captured. Treat this panel as "does my pattern match what I expect", not as a group inspector.
  2. An empty pattern is a valid pattern. With nothing typed, the regex matches the empty string at every position, which is why a fresh page over the sample text reports over a hundred matches and Total matched characters: 0. Match count alone is not evidence that your pattern works; the zero-length case is the giveaway to look for.

Replace: it is standard string replacement, $ included

Type a replacement and press Replace. The result box does not update while you type — that handler is deliberately inert — so the output is always from the last click.

The replacement field follows JavaScript's normal rules, which means $ is not literal:

You write It inserts
$& The matched text
$` / $' Everything before / after the match
$1, $2 … Capture group 1, 2 …
$$ A literal $

So $1 lets you reshape matches — (\d{4})-(\d{2})-(\d{2}) with $3/$2/$1 rewrites dates — and a replacement that legitimately starts with a dollar sign, like a currency amount, must be written $$12.50. If those rules are not the ones you had in mind, this is the place where they will bite.

Where this leaves your text

Both pages operate on what you paste, in the page. No content is sent anywhere for either comparison or matching, and neither page stores anything: reload and both boxes are back to their built-in samples.

Tools used in this guide

Advertisement