BLOG / GUIDE · 2026-05-21 · 9 min

What is natural sort? (file2 before file10, finally)

Why default sort puts file10 before file2, what natural sort does instead, and how to get it in Windows Explorer, macOS Finder, GNU sort, JavaScript, Python, and a one-click browser tool.

The short answer. Natural sort is what most people think sorting does. It puts file2.txt before file10.txt, treats embedded numbers as numbers rather than characters, and matches how Windows Explorer and macOS Finder have ordered files for two decades. Plain alphabetical sort doesn't do this — it compares character by character, so '1' beats '2' regardless of what follows. Natural sort needs explicit opt-in: sort -V on the command line, Intl.Collator({ numeric: true }) in JavaScript, the natsort library in Python, and the Natural mode in the joinlines list alphabetizer. The next section explains why every codebase that handles user-visible lists eventually hits this, and the rest of the article maps each platform to its natural-sort recipe.

Why default sort gets it wrong

Computers sort strings by comparing one character at a time, from left to right, using each character's underlying code point. For file2 vs file10:

  1. 'f' vs 'f' — same. Move on.
  2. 'i' vs 'i' — same.
  3. 'l' vs 'l' — same.
  4. 'e' vs 'e' — same.
  5. '2' vs '1''1' is less than '2'. Verdict: file10 comes first.

Character-by-character is fast, predictable, and correct for the original purpose of sorting (telephone directories, dictionary order). It's wrong every time a human looks at a list of filenames, version numbers, or numbered items.

Natural sort changes the rule: when both strings have a digit at the same position, consume the entire run of digits and compare the resulting numbers. For file2 vs file10, the comparison becomes 2 vs 10, not '2' vs '1'. Two beats ten in numeric comparison; file2 wins.

Three real cases where natural sort is what you want:

  • Filenames with sequence numbers. chapter1.md, chapter2.md, …, chapter11.md.
  • Version strings. v1.2.10 should come after v1.2.9, not before.
  • IP addresses, log levels, anything with embedded numbers where the number is meaningful.

In Windows Explorer (default since Windows XP)

Windows Explorer has used natural sort by default since Windows XP. The underlying API is StrCmpLogicalW, exposed in shlwapi.dll — "Compares two Unicode strings. Digits in the strings are considered as numerical content rather than text."

This means: when you list a folder with file1.txt, file2.txt, file10.txt, Explorer shows them in the order you'd expect. But Windows command-line tools (cmd.exe, dir, PowerShell's Get-ChildItem) use the original lexicographic sort — so a list that looks right in Explorer comes out "wrong" when you script it. PowerShell users who want natural-sort behaviour in scripts use Sort-Object with a custom comparer, or call StrCmpLogicalW directly via P/Invoke.

In macOS Finder (default behaviour)

macOS Finder has used natural sort by default since Mac OS X. Apple's sort uses ICU collation rules with numeric collation enabled — the same machinery JavaScript exposes via Intl.Collator. Filenames in Finder windows always appear naturally sorted.

Same caveat as Windows: the macOS command line tools (ls, find, sort) default to lexicographic order. A script that lists files in "Finder order" needs sort -V or an explicit collator.

On Linux and the command line

The GNU coreutils sort ships a dedicated flag for natural sort:

sort -V file.txt

-V is "version sort". Designed for software version strings (v1.10 after v1.2) but works on any string with embedded numbers. Variants:

  • sort -V file.txt — ascending natural sort.
  • sort -rV file.txt — descending.
  • sort -Vu file.txt — natural sort + deduplicate in one pass.

BSD sort on macOS does not support -V — the flag is a GNU extension. Install GNU coreutils with brew install coreutils and use gsort -V. On GNU coreutils, ls -v also does natural sort for directory listings — convenient when you want the natural order directly from a directory listing rather than piping through sort. BSD ls (macOS default) doesn't have -v; use gls -v from coreutils, or sort the output of plain ls through gsort -V.

Linux file managers (Nautilus on GNOME, Dolphin on KDE) use natural sort by default in their UI — matching Explorer and Finder behaviour.

In JavaScript (Intl.Collator)

Modern JavaScript exposes natural sort through Intl.Collator with the numeric: true option:

const collator = new Intl.Collator(undefined, { numeric: true });
const files = ["file10.txt", "file2.txt", "file1.txt"];
files.sort(collator.compare);
// ["file1.txt", "file2.txt", "file10.txt"]

The undefined first argument lets the runtime pick the user's locale. Pass a specific locale ("en-US", "de-DE") to lock the behaviour. Combine with sensitivity: "base" to ignore case + diacritics, or caseFirst: "upper" to control case ordering.

Under the hood, Intl.Collator uses ICU bindings — the same library that powers macOS Finder, every modern Linux desktop, and most server-side natural sort implementations. The behaviour is deterministic across browsers and Node.js once the locale is fixed. The joinlines list alphabetizer in Natural mode is a one-line wrapper around this exact API.

In Python (natsort library)

Python's built-in sorted() doesn't do natural sort — you need a third-party library:

from natsort import natsorted

files = ["file10.txt", "file2.txt", "file1.txt"]
natsorted(files)
# ['file1.txt', 'file2.txt', 'file10.txt']

Install with pip install natsort. The library handles signed numbers, decimals, version strings, locale-aware comparison, and case folding via flags. For one-off scripts where pulling in a dependency feels heavy:

import re

def natural_key(s):
    return [int(t) if t.isdigit() else t.lower()
            for t in re.split(r'(\d+)', s)]

sorted(files, key=natural_key)

Two lines of regex-based numeric tokenisation, no dependency. Works for most cases; the natsort library handles edge cases (decimals, signs, scientific notation) that the hand-rolled version doesn't.

In the browser, one-click

Paste the list into the list alphabetizer and pick Natural mode. The tool wraps Intl.Collator({ numeric: true }); the output matches Windows Explorer / macOS Finder behaviour.

Useful when:

  • You have a list of filenames pasted from an export and want them in natural order.
  • You're double-checking what Finder / Explorer would show without renaming files.
  • You want to send someone a naturally-sorted list without sending them the source file.

Pitfalls (the cases where natural sort surprises people)

Leading zeros

file001, file002, file010 sort the same in lexicographic and natural — the leading zeros make the numeric width consistent. Without leading zeros, only natural sort gets the order right. If you're generating a numbered filename list, padding with leading zeros is the cheapest cross-platform fix: file001.txt works correctly in every sort.

Negative numbers and decimals

Most natural-sort implementations don't parse signed numbers or decimals as a single unit. file-10 sorts the same as file10 with sort -V and Intl.Collator — the minus sign isn't treated as a sign. For full numeric awareness in Python:

from natsort import natsorted, ns
natsorted(items, alg=ns.SIGNED)

Or write a custom comparator that parses tokens explicitly.

Locale

"Natural" still depends on locale for the non-numeric parts. Ä sorts after Z in Swedish, next to A in German. Intl.Collator respects the locale you pass; sort -V respects LC_COLLATE. If your data is multilingual, pick the locale carefully and document the choice.

Mixed scripts and digits in non-ASCII

Arabic-Indic digits (٠١٢٣...) and full-width digits (0123...) look like digits but most natural-sort implementations don't recognise them. If your filenames mix scripts, normalise to ASCII digits first, or test against your data before assuming the sort is "natural".

When not to use natural sort

Three cases where lexicographic order is right and natural is wrong:

  • Strings with leading-zero IDs that you treat as opaque. User IDs like 0001Z, 0010A — lexicographic gives stable, predictable order; natural might reorder them surprisingly.
  • Anything where the embedded numbers don't mean a quantity. Phone numbers, postal codes, hash prefixes. Sorting 415-555-0100 "naturally" treats the area code as a number — usually not what you want.
  • Database indexes and binary protocols. If your downstream system uses byte order, sorting "naturally" beforehand only hides the real ordering and confuses debugging.

Frequently asked questions

Why don't Linux ls and Finder agree on file order?

ls defaults to lexicographic (POSIX-compliant); Finder defaults to natural. Use ls -v on Linux to match Finder behaviour. On macOS, BSD ls doesn't have -v — install GNU coreutils (brew install coreutils) and use gls -v, or rely on Finder for visual sorting.

Will natural sort fix my CSV row order?

Only if the rows are identified by a string with embedded numbers (like file1, file2). For CSVs sorted by a numeric column, use the column directly (sort -t, -k3n file.csv sorts numerically by column 3). Natural sort is for string keys with embedded numbers, not for already-numeric columns.

Is natural sort always slower than lexicographic?

Marginally. Intl.Collator with numeric: true is measurably slower than the default in microbenchmarks; sort -V is similar. For real-world list sizes (anything under a few million items), the difference is invisible. For massive batches in performance-critical paths, the gap matters, but the right answer there is usually to sort by a real numeric column rather than parsing digits inside strings.

How do natural sort and version sort differ?

Mostly the same, with one nuance. Version sort (sort -V) treats version-string conventions specifically — pre-release suffixes like 1.0-rc1 sort before 1.0, decimal points are part of the version. Natural sort (Intl.Collator, Windows StrCmpLogicalW) is more general — just "compare embedded numbers as numbers". For typical filename lists, the two produce identical output; for SemVer strings, sort -V has a small edge in correctness.

Why does my Python sorted() not do natural sort?

Because Python's standard library doesn't include natural sort. The design decision was that natural sort is a UI concern, not a data concern, so users opt in via natsort or a custom key= function. Other languages with built-in ICU bindings (JavaScript, Java, .NET) expose it more directly — but they also import a large I18N library to do it. Python keeps the core lean.

Is the joinlines list alphabetizer in Natural mode the same as Windows Explorer order?

Functionally equivalent for ASCII filenames. Both treat embedded digit runs as numbers; both fall back to lexicographic for non-digit characters. Edge cases (Arabic-Indic digits, full-width digits, locale-specific accent ordering) may diverge — the joinlines tool uses Intl.Collator with the browser's locale, Explorer uses StrCmpLogicalW with the Windows system locale. For 99% of real lists they agree exactly.

— the joinlines team, 2026-05-21

§ — PRIVACY Your text never leaves your browser. No upload, no account, no logs on what you paste.
PRIVACY PAGE ↗