An ISBN (International Standard Book Number) is a 13-digit identifier for a book edition that starts with 978 or 979 and ends with a check digit, for example “978-92-95055-12-4”. Books published before 2007 also carry a 10-digit form, ISBN-10, whose last character can be the letter X. A regex can check that a value has the shape of an ISBN, but only the check digit calculation tells us whether the number is valid, so a complete Java validator uses both.
This tutorial covers regular expressions for ISBN-10 and ISBN-13 in compact and formatted form, the check digit algorithm for each in Java, and converting an ISBN-10 to an ISBN-13. The examples use the sample ISBN from the official ISBN Users’ Manual, “978-92-95055-12-4”, along with “0-306-40615-2” and the made-up “123456789X”, which happens to have a correct check digit.
These are the main calls with their results on Java 25. compact(), isValidIsbn() and toIsbn13() are short methods defined in sections 3, 6 and 8.
compact("ISBN 978-92-95055-12-4"); // "9789295055124"
"9789295055124".matches("97[89][0-9]{10}"); // true, ISBN-13 shape
"123456789X".matches("[0-9]{9}[0-9X]"); // true, ISBN-10 shape
isValidIsbn("ISBN 978-92-95055-12-4"); // true
isValidIsbn("978-92-95055-12-5"); // false, wrong check digit
isValidIsbn("ISBN-10: 0-306-40615-2"); // true
isValidIsbn("123456789X"); // true
toIsbn13("0-306-40615-2"); // Optional[9780306406157]
1. How an ISBN Is Built
According to the ISBN Users’ Manual published by the International ISBN Agency, an ISBN-13 has five elements. Three of them have a variable length, which is why hyphens appear in different places in different ISBNs.
| Element | Example | Length |
|---|---|---|
| GS1 prefix | 978 | 3 digits: 978 or 979 |
| Registration group (country or language area) | 92 | 1 to 5 digits |
| Registrant (publisher) | 95055 | Up to 7 digits |
| Publication (title or edition) | 12 | Up to 6 digits |
| Check digit | 4 | 1 digit |
The older ISBN-10 has the same structure without the GS1 prefix, and its check digit is calculated differently, so it can be 0 to 9 or X (which stands for 10). Since 2007, agencies assign only 13-digit ISBNs, but ISBN-10 values still appear in older catalogs and on the back of older books.
The manual states that hyphens and spaces have no meaning and only make the number easier to read. “978-92-95055-12-4”, “978 92 95055 12 4” and “9789295055124” are the same ISBN, and printed ISBNs are usually preceded by the label “ISBN”.
2. Two Ways to Validate an ISBN
Because separators and the label are optional, an ISBN can be written in many ways. Code can handle that variety in one of two styles:
- One large regex that accepts every formatted variant and checks the number of elements. This is the approach of the original version of this article (section 7).
- Remove the label and the separators first, then check the 10 or 13 remaining characters with a short regex and the check digit.
We recommend the second approach. The regexes stay short, the check digit code works on clean input, and the stored value has one canonical form. The large regex is still useful when we must reject ISBNs whose hyphens are in impossible places, for example in a data import that has to match a strict format.
3. Regex for the Compact ISBN-10 and ISBN-13
The first step removes the optional label (“ISBN”, “ISBN-10” or “ISBN-13”, with an optional colon) and every hyphen and whitespace character. Converting to uppercase first makes a lowercase “x” check digit and an “isbn” label work too.
static final Pattern LABEL = Pattern.compile("^ISBN(?:-1[03])?:?\\s*");
static final Pattern SEPARATORS = Pattern.compile("[-\\s]");
static String compact(String input) {
String noLabel = LABEL.matcher(input.strip().toUpperCase()).replaceFirst("");
return SEPARATORS.matcher(noLabel).replaceAll("");
}
compact("ISBN-13: 978 92 95055 12 4"); // "9789295055124"
compact("isbn 978-92-95055-12-4"); // "9789295055124"
compact("ISBN-10 0-306-40615-2"); // "0306406152"
compact("12345-6789-x"); // "123456789X"
The compact forms then need only two short patterns:
| Form | Regex | Meaning |
|---|---|---|
| ISBN-10 | ^[0-9]{9}[0-9X]$ | Nine digits, then a digit or X |
| ISBN-13 | ^97[89][0-9]{10}$ | 978 or 979, then ten more digits |
static final Pattern ISBN10 = Pattern.compile("^[0-9]{9}[0-9X]$");
static final Pattern ISBN13 = Pattern.compile("^97[89][0-9]{10}$");
ISBN13.matcher("9789295055124").matches(); // true
ISBN13.matcher("9799295055124").matches(); // true, 979 is also an ISBN prefix
ISBN13.matcher("9779295055124").matches(); // false, 977 is not
ISBN10.matcher("X123456789").matches(); // false, X only in the last position
4. ISBN-10 Check Digit in Java
The ISBN-10 check uses a weighted sum. The first character is multiplied by 10, the second by 9, and so on down to the check character, which is multiplied by 1. The ISBN-10 is valid when this sum is divisible by 11, with X counted as 10. The Wikipedia article on ISBN describes the rule in detail.
For “0306406152”, the sum is 0x10 + 3×9 + 0x8 + 6×7 + 4×6 + 0x5 + 6×4 + 1×3 + 5×2 + 2×1 = 132, and 132 = 12 x 11, so the number is valid.
static boolean isValidIsbn10(String digits) {
if (!ISBN10.matcher(digits).matches()) {
return false;
}
int sum = 0;
for (int i = 0; i < 10; i++) {
char c = digits.charAt(i);
int value = (c == 'X') ? 10 : c - '0';
sum += value * (10 - i);
}
return sum % 11 == 0;
}
isValidIsbn10("0306406152"); // true
isValidIsbn10("0306406153"); // false, last digit changed
isValidIsbn10("0306406125"); // false, last two digits swapped
isValidIsbn10("123456789X"); // true, X counts as 10
isValidIsbn10("1234567890"); // false
The expression c – ‘0’ converts a digit character to its number: ‘7’ – ‘0’ is 7. Because 11 is a prime number, this check detects every single wrong digit and every swap of two adjacent digits.
5. ISBN-13 Check Digit in Java
The ISBN-13 check digit uses the EAN-13 barcode algorithm. The ISBN Users’ Manual describes it in its appendix: each of the first 12 digits is multiplied alternately by 1 and 3, and the check digit is 10 minus the remainder of the sum divided by 10 (a result of 10 becomes 0). To validate a complete ISBN-13, we include the check digit with weight 1 and test that the total is divisible by 10.
For “978-92-95055-12-?”, the weighted sum of the first 12 digits is 9 + 21 + 8 + 27 + 2 + 27 + 5 + 0 + 5 + 15 + 1 + 6 = 126. The remainder is 6, so the check digit is 10 – 6 = 4.
static boolean isValidIsbn13(String digits) {
if (!ISBN13.matcher(digits).matches()) {
return false;
}
int sum = 0;
for (int i = 0; i < 13; i++) {
int value = digits.charAt(i) - '0';
sum += (i % 2 == 0) ? value : value * 3;
}
return sum % 10 == 0;
}
static int isbn13CheckDigit(String first12) {
int sum = 0;
for (int i = 0; i < 12; i++) {
int value = first12.charAt(i) - '0';
sum += (i % 2 == 0) ? value : value * 3;
}
return (10 - sum % 10) % 10;
}
isbn13CheckDigit("978929505512"); // 4
isValidIsbn13("9789295055124"); // true
isValidIsbn13("9789295055125"); // false
isValidIsbn13("9789295055142"); // false, two digits swapped
isValidIsbn13("9789290555124"); // true, "50" swapped to "05" is not detected
That final call shows a known weakness of the ISBN-13 check. It finds every single wrong digit, but it misses a swap of two adjacent digits that differ by 5, such as “5” and “0”. This is a property of the standard, not a bug in the code.
6. A Complete ISBN Validator
With the compact form and both check digit methods, the full validator is a switch on the length:
static boolean isValidIsbn(String input) {
if (input == null) {
return false;
}
String digits = compact(input);
return switch (digits.length()) {
case 10 -> isValidIsbn10(digits);
case 13 -> isValidIsbn13(digits);
default -> false;
};
}
isValidIsbn("ISBN 978-92-95055-12-4"); // true
isValidIsbn("978 92 95055 12 4"); // true
isValidIsbn("0-306-40615-3"); // false
isValidIsbn("979-12-34567-89-6"); // true
This validator ignores the position of hyphens, so “978-92-95055124” passes. That matches the standard, where separators carry no meaning. It also does not compare the label with the length, so “ISBN-13 0-306-40615-2” is accepted as a valid ISBN-10. If a label must agree with the number, we check it separately.
Projects that already depend on Apache Commons Validator can use its ISBNValidator class instead. It checks both forms with their check digits and can convert ISBN-10 to ISBN-13. The code in this article has no dependencies and shows how the checks work.
7. One Regex for Formatted ISBNs
The original version of this article used the formatted regexes from the Regular Expressions Cookbook. They validate the written form in one step, including the number of elements, but not the check digit. The combined pattern for both forms is:
Pattern ISBN_FORMATTED = Pattern.compile(
"^(?:ISBN(?:-1[03])?:? )?(?=[0-9X]{10}$|(?=(?:[0-9]+[- ]){3})[- 0-9X]{13}$"
+ "|97[89][0-9]{10}$|(?=(?:[0-9]+[- ]){4})[- 0-9]{17}$)"
+ "(?:97[89][- ]?)?[0-9]{1,5}[- ]?[0-9]+[- ]?[0-9]+[- ]?[0-9X]$");
It reads in three parts:
| Part | Meaning |
|---|---|
| (?:ISBN(?:-1[03])?:? )? | Optional label “ISBN”, “ISBN-10” or “ISBN-13”, optional colon, then a space |
| (?=…) | A lookahead that checks the total length without consuming characters: 10 characters, or 13 with exactly 3 separators (ISBN-10), or 13 digits, or 17 characters with exactly 4 separators (ISBN-13) |
| (?:97[89][- ]?)?[0-9]{1,5}[- ]?[0-9]+[- ]?[0-9]+[- ]?[0-9X] | Optional 978 or 979 prefix, then the group, registrant, publication and check elements, each optionally followed by a hyphen or space |
The lookahead (?=(?:[0-9]+[- ]){4}) is the key part: it requires four “digits followed by a separator” runs before the end, so a formatted ISBN-13 must have all five elements. The project also contains the separate ISBN-10 and ISBN-13 versions of this pattern, and the article on regex metacharacters explains groups and lookaheads in general.
Comparing this regex with isValidIsbn() from section 6 shows what each one checks:
| Input | ISBN_FORMATTED | isValidIsbn() | Why |
|---|---|---|---|
| “ISBN 978-92-95055-12-4” | true | true | Valid |
| “ISBN-13: 978-92-95055-12-4” | true | true | Label with colon |
| “978 92 95055 12 4” | true | true | Spaces |
| “ISBN-10: 0-306-40615-2” | true | true | ISBN-10 |
| “0306406152” | true | true | Compact ISBN-10 |
| “978-92-95055-12-5” | true | false | Wrong check digit |
| “0-306-40615-3” | true | false | Wrong check digit |
| “ISBN-13 0-306-40615-2” | true | true | Label does not match the length |
| “ISBN-12: 978-92-95055-12-4” | false | false | Unknown label |
| “0-3061-40615-2” | false | false | 11 digits |
| “978-92-95055124” | false | true | Only 3 separators in an ISBN-13 |
| “ISBN 978–92-95055-12-4” | false | true | Double hyphen |
A wrong check digit passes the formatted regex, so it must always be combined with the check digit methods. The regex is stricter about separators, which matters only when the input format itself is part of the contract.
8. Converting Between ISBN-10 and ISBN-13
Every ISBN-10 has an ISBN-13 equivalent: we add the prefix 978, drop the old check digit and compute a new one with the ISBN-13 algorithm. The reverse works only for ISBNs that start with 978, because an ISBN-13 with the 979 prefix has no ISBN-10 form.
static Optional<String> toIsbn13(String isbn10) {
String digits = compact(isbn10);
if (!isValidIsbn10(digits)) {
return Optional.empty();
}
String first12 = "978" + digits.substring(0, 9);
return Optional.of(first12 + isbn13CheckDigit(first12));
}
toIsbn13("0-306-40615-2"); // Optional[9780306406157]
toIsbn13("123456789X"); // Optional[9781234567897]
toIsbn13("92-95055-12-X"); // Optional.empty, wrong check digit
toIsbn10("978-92-95055-12-4"); // Optional[9295055128]
toIsbn10("979-12-34567-89-6"); // Optional.empty, 979 has no ISBN-10
The toIsbn10() method in the project mirrors toIsbn13(): it takes digits 4 to 12 of a valid 978 ISBN and appends a new ISBN-10 check character.
9. ISBN Validator Source and Tests
The ISBN example on GitHub holds all the code from this article in one class, IsbnValidation. The main method prints every expression shown above, and the JUnit 6 tests check each result, including the full comparison table from section 7 and a loop that confirms every single-digit change to an ISBN-13 is detected. Compile it with JDK 25 through Maven.
mvn -q compile exec:java
mvn test
10. Conclusion
Once the label and separators are gone, two short regexes recognize an ISBN: ^[0-9]{9}[0-9X]$ for ISBN-10 and ^97[89][0-9]{10}$ for ISBN-13. The check digit decides validity: a weighted sum divisible by 11 for ISBN-10, and weights 1 and 3 with a sum divisible by 10 for ISBN-13. The single formatted regex from the Regular Expressions Cookbook still has a place when the written format must be enforced, but it never replaces the check digit. Converting ISBN-10 values to ISBN-13 before storing them leaves one form to search and compare.
11. References
The Users’ Manual is the authoritative description of the ISBN structure and the ISBN-13 check digit.
- ISBN Users’ Manual (International ISBN Agency)
- ISBN: check digits (Wikipedia)
- ISBNValidator (Apache Commons Validator)
- Pattern (Java SE 25 API)
Happy Learning !!