Luhn算法校验逻辑是:从右往左,奇数位直接累加,偶数位乘2后若≥10则减9,总和模10为0即通过;它仅验证数字序列校验和规则,不判断卡类型或真实性。

什么是Luhn算法的校验逻辑
Luhn算法不是用来“识别卡类型”或“联网验证”的,它只做一件事:检查一串数字是否满足基础校验和规则。很多开发者误以为通过Luhn就等于“是真实信用卡”,其实它连格式合法性(比如长度、前缀)都不覆盖——4532015112830366可能通过Luhn,但4532015112830367就会失败,仅此而已。
核心步骤只有三步:从右往左,奇数位(第1、3、5…位)直接累加;偶数位乘2,若结果≥10则减9(等价于各位相加);总和模10为0即通过。
如何用C++实现Luhn校验函数
关键在于字符串预处理和索引方向。别从左往右遍历,也别用std::stoi转整数——信用卡号常超long long范围(如19位Diners Club),必须按字符逐位处理。
- 先用
std::remove_if+std::isdigit清理空格、短横线等非数字字符 - 检查清理后长度是否在13–19之间(主流卡范围),长度非法直接返回
false - 从末位开始倒序遍历:
for (int i = cleaned.size() - 1; i >= 0; --i) - 用
(cleaned[i] - '0')转数字,用(pos % 2 == 0)判断是否为“从右数的奇数位”(注意:最右是位置0)
bool isValidCreditCard(const std::string& s) {
std::string cleaned;
std::copy_if(s.begin(), s.end(), std::back_inserter(cleaned), ::isdigit);
if (cleaned.empty() || cleaned.size() < 13 || cleaned.size() > 19) return false;
<pre class="brush:php;toolbar:false;">int sum = 0;
bool doubleDigit = false;
for (int i = cleaned.size() - 1; i >= 0; --i) {
int digit = cleaned[i] - '0';
if (doubleDigit) {
digit *= 2;
if (digit > 9) digit -= 9;
}
sum += digit;
doubleDigit = !doubleDigit;
}
return sum % 10 == 0;}
立即学习“C++免费学习笔记(深入)”;
常见错误:空格、符号、全角字符导致校验失败
用户输入"4532-0151-1283-0366"或"4532015112830366"(全角数字)时,isdigit在默认locale下对后者返回false,整个字符串被清空,最终判为无效——这不是算法问题,是输入清洗没到位。
- 若需支持全角数字,改用
std::iswdigit配合std::wstring_convert,但代价高;更实际的做法是提前拒绝非ASCII数字 - 别用
std::regex清理,简单场景下性能差且易写错模式(比如"\D"会误删+但漏掉不换行空格) - 测试用例务必包含:
"4532015112830366"(通过)、"4532015112830367"(失败)、" 4532-0151-1283-0366 "(应通过)
为什么不能只靠Luhn判断“是否为信用卡号”
Luhn只是防呆校验,不是身份认证。它无法区分4532015112830366(Visa样例)和378282246310005(Amex样例)是否真实发卡——两者都可通过Luhn,但前者可能是测试号,后者甚至已被作废。
- 真正生产环境要结合BIN/IIN前6位查卡组织表(如
453201属于Visa,378282属于Amex) - 前端可做Luhn快速反馈,但后端必须走支付网关API验证(如Stripe的
/tokens端点) - 别把Luhn结果存进数据库字段标为“valid_card”,它只代表“数字序列符合Luhn规则”
最常被忽略的一点:Luhn对全零字符串"0000000000000000"返回true——它合法,但显然不是有效卡号。业务逻辑里得额外拦截这种极端值。



















