How the comparison works
The texts are split into lines and compared with Eugene Myers' O(ND) diff algorithm, the one behind GNU diff, git and DiffPlex. It finds a shortest edit script: the fewest deleted and inserted lines that turn the original into the modified text. When several scripts are equally short, this page picks the same one as DiffPlex, because it follows DiffPlex's implementation step by step.
A block of deleted lines followed by inserted lines is shown as changed lines, paired in order: the first deleted line next to the first inserted one, and so on. The extra lines of a longer side are plain additions or removals. Inside each changed pair, a second diff over the words (or characters) marks exactly what changed. These are the counts at the top of the result:
| Count | Meaning |
|---|---|
| added | Inserted lines with no deleted line to pair with. |
| removed | Deleted lines with no inserted line to pair with. |
| changed | Deleted and inserted lines paired with each other (DiffPlex's ChangeType.Modified). |
| edit distance | All deleted plus all inserted lines: the length of the shortest edit script. |
The options
- Ignore at start and end of lines trims each line before comparing, with the whitespace characters of .NET's
char.IsWhiteSpace. This is DiffPlex'signoreWhiteSpace: true. - Ignore all whitespace removes every whitespace character, so
x=1andx = 1are equal, likediff -wandgit diff -w. DiffPlex has no such option. - Ignore case compares like
StringComparison.OrdinalIgnoreCase(DiffPlex'signoreCase: true): each letter is upper-cased on its own, soßdoes not equalSS. - Ignore line endings treats CRLF, LF and CR line breaks as equal, and a missing newline at the end of the text too. When it is off, a line whose only change is its line break is a changed line, marked CRLF, LF, CR or "no newline at end". A text box always gives LF line breaks, so open files to compare their line endings.
The whitespace and case options also apply to the word highlighting, so a changed line can show no highlighted words when only its whitespace changed.
Compare text in C# with DiffPlex
DiffPlex is the diff library most .NET tools use. This code prints the same inline diff, counts and edit distance as this page (with Ignore line endings on, see below):
using System.Text;
using DiffPlex;
using DiffPlex.DiffBuilder;
using DiffPlex.DiffBuilder.Model;
// dotnet add package DiffPlex
static string DiffText(string oldText, string newText)
{
var output = new StringBuilder();
// One list of lines: "- " deleted, "+ " inserted, " " unchanged.
// ignoreWhiteSpace trims each line before comparing, and it defaults to true.
var inline = InlineDiffBuilder.Diff(oldText, newText, ignoreWhiteSpace: false, ignoreCase: false);
foreach (var line in inline.Lines)
{
var prefix = line.Type switch
{
ChangeType.Inserted => "+ ",
ChangeType.Deleted => "- ",
_ => " ",
};
output.Append(prefix).Append(line.Text).Append('\n');
}
// Two columns: a deleted line next to an inserted one is Modified.
var sideBySide = SideBySideDiffBuilder.Diff(oldText, newText, ignoreWhiteSpace: false, ignoreCase: false);
int added = sideBySide.NewText.Lines.Count(l => l.Type == ChangeType.Inserted);
int removed = sideBySide.OldText.Lines.Count(l => l.Type == ChangeType.Deleted);
int changed = sideBySide.OldText.Lines.Count(l => l.Type == ChangeType.Modified);
output.Append($"{added} added, {removed} removed, {changed} changed\n");
// The raw edit script: blocks of deleted and inserted lines.
var diff = Differ.Instance.CreateLineDiffs(oldText, newText, ignoreWhitespace: false, ignoreCase: false);
int edits = diff.DiffBlocks.Sum(b => b.DeleteCountA + b.InsertCountB);
output.Append($"{diff.DiffBlocks.Count} blocks, edit distance {edits}\n");
return output.ToString();
}
Things to know, all checked against DiffPlex 1.9:
InlineDiffBuilder.DiffandSideBySideDiffBuilder.Diffdefault toignoreWhiteSpace: true, so leave it out and a change of indentation is not a change. Passfalseto see it.- DiffPlex splits lines on
\r\n,\rand\nand drops the line breaks, so it always ignores line endings. - A text that ends with a newline gets one more, empty, line in DiffPlex:
"a\nb\n"isa,band an empty line. This page does not count that line, so DiffPlex matches the page when you pass both texts without their final newline. - For a character-level diff, pass a different chunker:
new SideBySideDiffBuilder(Differ.Instance, new LineChunker(), new CharacterChunker()). DiffPlex'sCharacterChunkersplits UTF-16 code units, which cuts an emoji in two; this page splits whole characters.
Unified diff, patch and git diff --no-index
The patch below the result is in the unified format of diff -u, with 3 lines of context and \ No newline at end of file where a text has no final newline. Apply it to the original file with patch -p1 < changes.patch. For files on disk you do not need this page:
git diff --no-index original.txt modified.txt
git diff --no-index -w --ignore-cr-at-eol original.txt modified.txt
git diff --no-index --word-diff original.txt modified.txt
diff -u original.txt modified.txt > changes.patch
git diff --no-index compares any two files, even outside a repository. -w ignores whitespace, --ignore-cr-at-eol ignores CRLF and LF differences, and --word-diff shows changed words inline. With an ignore option on, the patch here keeps the original's version of lines that only differ in what is ignored, like diff -u -w, so applying it does not reproduce those differences.
Large texts
The diff runs in a background thread, so the page stays responsive while it works: two 5,000-line files with scattered changes take a fraction of a second. Myers' algorithm gets slower as the texts get more different. If two large texts share almost nothing, the page stops searching for the shortest script after a fixed amount of work and shows the rest as replaced lines. The result is still a correct diff, just not the shortest one, and the page says so.
FAQ
How do I compare two texts in C#?
Install DiffPlex and call InlineDiffBuilder.Diff(oldText, newText) for one list of lines, or SideBySideDiffBuilder.Diff for two columns with changed words, as in the code above. Differ.Instance.CreateLineDiffs gives the raw blocks.
Why does DiffPlex say two texts are equal when the indentation differs?
ignoreWhiteSpace defaults to true in InlineDiffBuilder.Diff and SideBySideDiffBuilder.Diff. It trims each line before comparing. Pass ignoreWhiteSpace: false.
What is the edit distance?
The number of deleted lines plus the number of inserted lines in the shortest edit script that turns the original into the modified text. The diff here is always a shortest one, the same length as GNU diff --minimal finds.
How do I compare two files with git?
git diff --no-index old.txt new.txt compares any two files, even outside a repository. Add -w to ignore whitespace, --ignore-cr-at-eol to ignore CRLF and LF differences, and --word-diff to see changed words.
Why does a line show as changed when it looks the same?
Its line break differs (CRLF against LF), the last line has no newline on one side, or it differs in whitespace you cannot see, such as a tab against spaces or a no-break space. The line-break case is marked on the line; turn on Ignore line endings or a whitespace option to hide the others.
Is this the same diff git shows?
It changes the same number of lines, because both find a shortest edit script. When several scripts are equally short, git and this page can pick different lines, for example which of two identical closing braces moved.
Is my text uploaded?
No. The comparison runs in your browser.