一种基于正则表达式的验证ISBN-10为字符串的方法.根据那些讲英语的出版物(由下面代码中的正则表达式定义),可以是数字(或末尾的“X”),也可以是虚线。
最后一个数字,一个检查数字(除非它是'X'),是以下列方式计算的。从最左边的数字开始,将每一个数字乘以一个权重,然后对结果进行求和。检查数字应该是这样的,这个和可以被11整除。权重从1开始,每个数字增加1。例如,考虑ISBN 0-306-40615-2.这笔款项计算如下:
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.我是一个.NET开发人员,为了好玩而涉足C++。通过这个练习,我试图通过在namespace中创建一个匿名namespace来隐藏某些东西。本质上,我试图实现private关键字在C#中所做的工作,即C++语义。我很好奇我是否在这段代码中做过"un C++“之类的事情。我设想这段代码可以扩展为具有一个验证ISBN-13数字的函数。
#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;
}
}发布于 2020-11-01 11:21:36
当您比较8种不同的模式,然后简单地删除其中7种验证的-时,为什么不首先删除-,然后根据剩下的唯一模式进行验证。
另外要注意的是,在模式的末尾,您有一个字符集:[\d|\X]。这实际上将匹配以下之一:
|X字符(但您不需要\X )。相反,这应该是:
\d{9}[\dX]代码应如何工作的总体大纲:
-X发布于 2020-11-01 22:51:58
hjpotter92从正确的想法开始:
统一和简化。
你应该走得更远。
另外,如果最后一个数字是X,不要放弃,这对于您的代码来说是一个非常好的数字。
你对所有的破折号都不感兴趣(或者不应该),所以忽略它们。
你能知道你有所有用过的图案吗?
仔细看看ISBN10 10上的维基百科,您会发现这种模式是完全正常的。每一个数字都乘以它的位置,甚至最后一个数字(可能是10,用X表示)。
由于不需要修改字符串,所以可以使用std::string_view,使任何调用者都能获得最大的方便和效率。
是的,它只随C++17一起提供,如果您的库不提供它,那么要么使用免费的实现,要么至少返回到std::string const&以避免复制。
避免不必要的分配(即使小对象优化可能为您的助手函数的字符串参数节省您的培根)是一个非常C++的事情。
这并不是说它可以修复您创建的一个完全多余的std::vector。
现在您可以将其标记为noexcept和constexpr,使调用方确信它始终会成功,甚至可以在编译时完成。
顺便说一句,从C++到C++11,for-range是一件很好的事情,它比手动迭代器更好。
如果您多次使用不同的数据执行相同的操作,请考虑将其存储在数组中并循环使用,就像在C#中所做的那样。
constexpr bool validate_isbn10(std::string_view s) noexcept {
unsigned num = 0; // unsigned so overflow is harmless
std::size_t found = 0; // std::size_t so cannot overflow at all
for (auto c : s)
if (c >= '0' && c <= '9')
num += ++found * (c - '0');
else if (c == 'X' && found == 9)
num += ++found * 10;
else if (c != '-')
return false;
return found == 10 && num % 11 == 0;
}https://codereview.stackexchange.com/questions/251415
复制相似问题