Description
A regex-based method of validating ISBN-10 as strings. Can be digits (or 'X' at the end) only, or with dashes, according to those of English-speaking publications (which are defined by the regular expressions contained in the code below).
The last digit, a check digit (unless it is an 'X'), is calculated in the following manner. Multiply each digit, starting from the leftmost digit, by a weight, and then sum the results. The check digit should be such that this sum is divisible by 11. The weight starts at 1, and increases by 1 for each digit. For example, consider the ISBN 0-306-40615-2. The sum is calculated as follows:
sum = 0*1 + 3*2 + 0*3 + 6*4 + 4*5 + 0*6 + 6*7 + 1*8 + 5*9 + 2*10 = 165
165 mod 11 = 0 // the check digit, 2, is valid.
I am a .NET developer dabbling in C++ for fun. With this exercise, I have tried to hide certain things from anything outside the namespace
, by creating an anonymous namespace
inside it. Essentially, I have tried to achieve what the private
keyword does in C#, in C++ semantics. I am curious if I have done anything "un C++ like" in this code. I envisage this code could be expanded to have a function that validates ISBN-13 numbers.
Code
#include <iostream>
#include <string>
#include <vector>
#include <regex>
#include <algorithm>
namespace isbn_validation
{
namespace // anonymous namespace
{
// regex expressions
// dashless
const std::regex isbn10_no_dashes(R"((\d{9})[\d|\X])");
// with dashes
const std::regex isbn10_dashes1(R"((\d{1})\-(\d{5})\-(\d{3})\-[\d|\X])");
const std::regex isbn10_dashes2(R"((\d{1})\-(\d{3})\-(\d{5})\-[\d|\X])");
const std::regex isbn10_dashes3(R"((\d{1})\-(\d{4})\-(\d{4})\-[\d|\X])");
const std::regex isbn10_dashes4(R"((\d{1})\-(\d{5})\-(\d{3})\-[\d|\X])");
const std::regex isbn10_dashes5(R"((\d{2})\-(\d{5})\-(\d{2})\-[\d|\X])");
const std::regex isbn10_dashes6(R"((\d{1})\-(\d{6})\-(\d{2})\-[\d|\X])");
const std::regex isbn10_dashes7(R"((\d{1})\-(\d{7})\-(\d{1})\-[\d|\X])");
bool isbn10_check_digit_valid(std::string isbn10)
{
auto valid = false;
// split it
std::vector<char> split(isbn10.begin(), isbn10.end());
// if the very last character is an 'X', don't bother with it
if (split[9] == 'X')
{
return true;
}
// all digits
// validate the last digit (check digit)
int digit_sum = 0;
int digit_index = 1;
for (std::vector<char>::iterator it = split.begin(); it != split.end(); ++it)
{
digit_sum = digit_sum + ((*it - '0')*digit_index);
digit_index++;
}
valid = !(digit_sum%11);
return valid;
}
}
bool valid_isbn10(std::string isbn)
{
// can take ISBN-10, with or without dashes
auto valid = false;
// check if it is a valid ISBN-10 without dashes
if (std::regex_match(isbn, isbn10_no_dashes))
{
// validate the check digit
valid = isbn10_check_digit_valid(isbn);
}
// check if it is a valid ISBN-10 with dashes
if (std::regex_match(isbn, isbn10_dashes1) || std::regex_match(isbn, isbn10_dashes2) || std::regex_match(isbn, isbn10_dashes3) ||
std::regex_match(isbn, isbn10_dashes4) || std::regex_match(isbn, isbn10_dashes5) || std::regex_match(isbn, isbn10_dashes6) || std::regex_match(isbn, isbn10_dashes7))
{
// remove the dashes
isbn.erase(std::remove(isbn.begin(), isbn.end(), '-'), isbn.end());
// validate the check digit
valid = isbn10_check_digit_valid(isbn);
}
return valid;
}
}