Regex for Misspelled Words: Match Every Spelling in Java

Write a regex for misspelled words in Java with character classes, optional letters and alternation, then find and fix every spelling in one pass.

The word calendar with each uncertain position replaced by a character class, next to the four regex techniques for spelling variants

A regex for misspelled words lists, letter by letter, the spellings we expect people to type, so one pattern matches the correct word and its common mistakes. Writers often swap the vowels a and e in calendar, so the pattern c[ae]l[ae]nd[ae]r matches calendar, calandar, calender and calander. With the Java classes Pattern and Matcher, the same pattern can find, list or correct those words in a text.

We use misspelling patterns in content checks before publishing, in search boxes that should still find a product when the user misspells it, and in data cleanup jobs that fix known typos in imported records. The following example applies the four techniques to real words and corrects a sentence.

boolean swapped = "calandar".matches("c[ae]l[ae]nd[ae]r");          // true
boolean swapped2 = "calender".matches("c[ae]l[ae]nd[ae]r");         // true
boolean unplanned = "kalendar".matches("c[ae]l[ae]nd[ae]r");        // false, not a planned mistake
boolean optional = "colour".matches("colou?r");                     // true, optional letter
boolean doubled = "accomodate".matches("ac{1,2}om{1,2}odate");      // true, one or two of each letter
boolean order = "recieve".matches("rec(?:ei|ie)ve");                // true, either order

Pattern calendar = Pattern.compile("(?i)\\bc[ae]l[ae]nd[ae]r\\b");
String fixed = calendar.matcher("Check the calender.").replaceAll("calendar");   // "Check the calendar."

Notice that kalendar fails, because only the planned mistakes are in the pattern. We cover the four regex techniques for spelling variants, searching a text, whole words and case, and replacing every misspelling while keeping the case of the first letter. The article ends with the limits of the approach and the alternative for typos that nobody can predict, and the Java regex guide has the Pattern basics.

1. Four Ways to Describe a Spelling Variant

Each common spelling mistake maps to one regex construct. We look at the correct word, mark each position where people go wrong, and replace only that position.

The word calendar with each uncertain position replaced by a character class, next to the four regex techniques for spelling variants
Only the positions where people go wrong change; every other letter stays literal
MistakeRegex constructPatternMatches
One letter replaced by anotherCharacter classgr[ae]ygray, grey
A letter left outOptional letter ?colou?rcolor, colour
A double letter written singleRepeat count {1,2}ac{1,2}om{1,2}odateaccommodate, accomodate
Letters in the wrong orderAlternation (?:…|…)rec(?:ei|ie)vereceive, recieve
List<String> grays = Pattern.compile("\\bgr[ae]y\\b").matcher("gray grey gry").results().map(MatchResult::group).toList();   // [gray, grey]

boolean color = "color".matches("colou?r");                         // true
boolean colour = "colour".matches("colou?r");                       // true
boolean oneC = "acommodate".matches("ac{1,2}om{1,2}odate");         // true
boolean oneEach = "acomodate".matches("ac{1,2}om{1,2}odate");       // true
boolean threeM = "accommmodate".matches("ac{1,2}om{1,2}odate");     // false, three m's
boolean ieOrder = "recieve".matches("rec(?:ei|ie)ve");              // true
boolean eeTypo = "receeve".matches("rec(?:ei|ie)ve");               // false

The techniques combine in one pattern. Writers type definitely as definately, definitly or definatly, so the class [ia] covers the swapped vowel and e? covers the missing e.

boolean definately = "definately".matches("defin[ia]te?ly");      // true
boolean definatly = "definatly".matches("defin[ia]te?ly");        // true
boolean defiantly = "defiantly".matches("defin[ia]te?ly");        // false, a different word

Alternation also handles variants that differ by several letters, such as names. The pattern \bSte(?:ven?|phen)\b matches Steve, Steven and Stephen, but not Stefan.

List<String> names = Pattern.compile("\\bSte(?:ven?|phen)\\b").matcher("Steve, Steven, Stephen, Stefan").results().map(MatchResult::group).toList();   // [Steve, Steven, Stephen]

2. Finding Every Spelling in a Text

To find the words in a longer text, we call Matcher.find() in a loop. The methods start() and end() return the position of each match, which helps when we want to highlight or report it. The CASE_INSENSITIVE flag also matches words written with capital letters.

String notes = "This is may calandar. This is june calander. This is may calendar.";
Matcher m = Pattern.compile("c[ae]l[ae]nd[ae]r", Pattern.CASE_INSENSITIVE).matcher(notes);

while (m.find()) {
    System.out.println("Start index: " + m.start() + " End index: " + m.end() + " " + m.group());
}

The program prints each spelling with its position.

Start index: 12 End index: 20 calandar
Start index: 35 End index: 43 calander
Start index: 57 End index: 65 calendar

3. Whole Words, Plurals and Case

A misspelling pattern without word boundaries also matches inside longer words. In calendars, c[ae]l[ae]nd[ae]r finds calendar and leaves the s behind. Adding \b on both sides limits the pattern to whole words, and s? before the final \b accepts the plural.

String text = "calendars and a calender";

List<String> inside = Pattern.compile("c[ae]l[ae]nd[ae]r").matcher(text).results().map(MatchResult::group).toList();         // [calendar, calender]
List<String> whole = Pattern.compile("\\bc[ae]l[ae]nd[ae]r\\b").matcher(text).results().map(MatchResult::group).toList();    // [calender]
List<String> plural = Pattern.compile("\\bc[ae]l[ae]nd[ae]rs?\\b").matcher(text).results().map(MatchResult::group).toList(); // [calendars, calender]

The first list contains calendar because the pattern matched the first eight letters of calendars. Without a flag, the pattern is case-sensitive, so Calendar at the start of a sentence does not match until we add (?i) or Pattern.CASE_INSENSITIVE.

boolean capital = "Calendar".matches("c[ae]l[ae]nd[ae]r");            // false
boolean ignoreCase = "Calendar".matches("(?i)c[ae]l[ae]nd[ae]r");       // true

The article Regex word boundaries explains how \b decides where a word starts and ends.

4. Listing Only the Misspelled Words

The pattern matches the correct spelling too. A blog that runs a spelling check before an article goes live wants a report of the mistakes only, so we filter out the correct word after matching.

Pattern calendar = Pattern.compile("(?i)\\bc[ae]l[ae]nd[ae]r\\b");

List<String> typos = calendar.matcher("calendar, calender, Calendar").results().map(MatchResult::group).filter(w -> !w.equalsIgnoreCase("calendar")).toList();   // [calender]

5. Replacing Misspellings With the Correct Word

The method Matcher.replaceAll() replaces every match. With a fixed replacement, every match becomes calendar in lowercase, and a plural that the pattern does not cover stays unchanged.

Pattern calendar = Pattern.compile("(?i)\\bc[ae]l[ae]nd[ae]r\\b");
String text = "Check the calender. Your Calandars are full.";

String lowered = calendar.matcher(text).replaceAll("calendar");      // "Check the calendar. Your Calandars are full."

To keep the case of the first letter, we capture it in a group and put it back with $1 in the replacement. A second group captures the optional s.

String text = "Check the calender. Your Calandars are full.";
String kept = text.replaceAll("\\b([cC])[ae]l[ae]nd[ae]r(s?)\\b", "$1alendar$2");   // "Check the calendar. Your Calendars are full."

Wikipedia corrects typos with the same technique. Its list of typo rules (RegExTypoFix) holds several thousand rules, and most of them use a capture group for the first letter, such as \b([cC])alander(s)?\b replaced by $1alendar$2. Some rules also contain a lookbehind that skips a real name spelled like the typo, because a correct word can look like a mistake.

6. Writing Long Patterns Readably

Misspelling patterns grow fast, and a long pattern on one line is hard to review. The COMMENTS flag, inline (?x), ignores spaces and line breaks in the pattern and treats everything after # as a comment. A Java text block holds the pattern on several lines.

Pattern commented = Pattern.compile("""
    (?x)            # comments mode, spaces and # comments are ignored
    \\b c [ae]      # c, then a or e
    l [ae]          # l, then a or e
    nd [ae] r \\b   # nd, then a or e, then r
    """);

boolean readable = commented.matcher("calender").matches();          // true

In comments mode, a real space or # in the word must be escaped as \ or \#, or written inside a character class.

7. Limits of a Regex for Misspelled Words

A misspelling pattern matches only the mistakes we planned for, and it may accept a few strings nobody types. Both effects show up with calendar.

  • The pattern c[ae]l[ae]nd[ae]r also accepts celendar, because the first class allows e. The extra match rarely causes harm, but in other words an extra spelling can be a different, correct word.
  • The pattern rejects calendr and kalendar, because the missing a and the k were not in the plan. We extend the pattern when real data shows a new mistake.
boolean rare = "celendar".matches("c[ae]l[ae]nd[ae]r");             // true, accepted although rare
boolean missingA = "calendr".matches("c[ae]l[ae]nd[ae]r");           // false
boolean withK = "kalendar".matches("[ck][ae]l[ae]nd[ae]?r");         // true, extended pattern
boolean extended = "calendr".matches("[ck][ae]l[ae]nd[ae]?r");       // true

For typos that cannot be predicted, an edit-distance check works better than a regex. The Levenshtein distance counts the single-letter insertions, deletions and replacements between two words, so calendr is one edit away from calendar. The LevenshteinDistance class in Apache Commons Text computes it.

int oneEdit = LevenshteinDistance.getDefaultInstance().apply("calendr", "calendar");     // 1
int twoEdits = LevenshteinDistance.getDefaultInstance().apply("kalendr", "calendar");    // 2

A regex stays the better choice for a short, known list of variants, because it needs no dictionary and can find and replace the words in one pass.

8. Example Code and How to Run It

The misspellings project on GitHub contains every pattern from this article and prints its result. JUnit 6 tests check the accepted and rejected spellings, the match positions and the corrected text. It runs on Java 25 with Maven.

mvn -q compile exec:java
mvn test

9. Misspelling Regex FAQs

Readers who try the calendar pattern on their own data come back with these questions.

9.1. How Do We Correct Several Misspelled Words in One Pass?

We join the typo patterns into one alternation and look up the correct word for each match in a map. The method replaceAll(Function), available since Java 9, computes each replacement from the match.

Map<String, String> fixes = Map.of("calender", "calendar", "recieve", "receive", "definately", "definitely");
Pattern typo = Pattern.compile("\\b(?:" + String.join("|", fixes.keySet()) + ")\\b");
String corrected = typo.matcher("I recieve the calender definately").replaceAll(r -> fixes.get(r.group()));   // "I receive the calendar definitely"

9.2. Can a Regex Fix Any Spelling Mistake?

No. A regex fixes only the mistakes written into it, so a spelling checker uses a dictionary and edit distance to find unknown typos. The regex approach fits a fixed list of words that matter to the app, such as product or brand names.

9.3. Should We Use (?i) or Pattern.CASE_INSENSITIVE?

Both give the same result. The inline (?i) works with String.matches() and replaceAll(), which accept only a pattern string, while the flag constant fits patterns compiled once in a static final field. For words with letters beyond ASCII, we add UNICODE_CASE as well.

10. Conclusion

To match a word together with its common misspellings, we replace each uncertain position with a character class ([ae]), an optional letter (u?), a repeat count (c{1,2}) or an alternation ((?:ei|ie)).

Word boundaries and (?i) keep the search to whole words in any case, and a capture group lets replaceAll() fix the spelling while keeping a capital first letter. For unpredictable typos, an edit-distance check complements the regex. The Java regex metacharacters reference explains each of these constructs in detail.

11. References

Happy Learning !!

Source Code on Github

About Us

HowToDoInJava provides tutorials and how-to guides on Java and related technologies.

It also shares the best practices, algorithms & solutions and frequently asked interview questions.