验证 Luhn 校验和的实现有很多,但生成它们的实现却很少。我遇到过这个,但是在我的测试中它显示出有问题,而且我不理解 delta 变量背后的逻辑。
我已经制作了这个函数,据说应该生成 Luhn 校验和,但由于某种原因,我还不明白生成的校验和有一半时间是无效的。
function Luhn($number, $iterations = 1)
{
while ($iterations-- >= 1)
{
$stack = 0;
$parity = strlen($number) % 2;
$number = str_split($number, 1);
foreach ($number as $key => $value)
{
if ($key % 2 == $parity)
{
$value *= 2;
if ($value > 9)
{
$value -= 9;
}
}
$stack += $value;
}
$stack = 10 - $stack % 10;
if ($stack == 10)
{
$stack = 0;
}
$number[] = $stack;
}
return implode('', $number);
}
一些例子:
Luhn(3); // 37, invalid
Luhn(37); // 372, valid
Luhn(372); // 3728, invalid
Luhn(3728); // 37283, valid
Luhn(37283); // 372837, invalid
Luhn(372837); // 3728375, valid
我正在验证生成的校验和针对此页面,我在这里做错了什么?
为了将来参考,这里是工作函数。
function Luhn($number, $iterations = 1)
{
while ($iterations-- >= 1)
{
$stack = 0;
$number = str_split(strrev($number), 1);
foreach ($number as $key => $value)
{
if ($key % 2 == 0)
{
$value = array_sum(str_split($value * 2, 1));
}
$stack += $value;
}
$stack %= 10;
if ($stack != 0)
{
$stack -= 10;
}
$number = implode('', array_reverse($number)) . abs($stack);
}
return $number;
}
我删除了 $parity 变量,因为我们不需要它用于此目的,并验证:
function Luhn_Verify($number, $iterations = 1)
{
$result = substr($number, 0, - $iterations);
if (Luhn($result, $iterations) == $number)
{
return $result;
}
return false;
}
编辑:抱歉,我现在意识到您几乎已经有了我的全部答案,您只是错误地确定了哪个数字用于哪个因素。
我现在的整个回答可以用这一句话来概括:
你把因子颠倒了,你根据数字的长度将错误的数字乘以 2。
您的校验和一半时间无效的原因是,在您的检查中,您的号码有一半时间为奇数位,然后您将错误的数字加倍。
对于 37283,从右边数起,您会得到以下数字序列:
3 * 1 = 3 3
8 * 2 = 16 --> 1 + 6 = 7
2 * 1 = 2 2
7 * 2 = 14 --> 1 + 4 = 5
+ 3 * 1 = 3 3
= 20
该算法要求您将原始数字中的各个数字以及“从右侧开始的每两位数字”的乘积的各个数字相加。
因此,从右边开始,将 3 + (1 + 6) + 2 + (1 + 4) + 3 相加,得到 20。
如果您最终得到的数字以零结尾(20 就是这样),则该数字是有效的。
现在,您的问题暗示您想知道如何生成校验和,嗯,这很简单,请执行以下操作:
例如:号码是12345
计算 123450 的 luhn 校验和,结果为
0 5 4 3 2 1
1 2 1 2 1 2 <-- factor
0 10 4 6 2 2 <-- product
0 1 0 4 6 2 2 <-- sum these to: 0+1+0+4+6+2+2=15
求和 (15),模 10,得到 5
所以结果是123455。
你的 php 有 bug,它会导致无限循环。 这是我正在使用的工作版本,根据您的代码进行了修改
函数 Luhn($number) {
$stack = 0; $number = str_split(strrev($number)); foreach ($number as $key => $value) { if ($key % 2 == 0) { $value = array_sum(str_split($value * 2)); } $stack += $value; } $stack %= 10; if ($stack != 0) { $stack -= 10; $stack = abs($stack); } $number = implode('', array_reverse($number)); $number = $number . strval($stack); return $number;
}
创建一个 php 并在本地主机 Luhn(xxxxxxxx) 中运行以确认。
不好
我简直不敢相信有多少糟糕的实现。
IDAutomation 有一个带有 MOD10() 函数的 .NET 程序集可供创建,但它似乎不起作用。在 Reflector 中,代码对于它应该做的事情来说太长了。
不好
这个混乱的页面实际上当前从维基百科(!)链接到Javascript,它有几个验证实现,当我调用每个验证实现时,它们甚至不返回相同的值。
好
从维基百科的 Luhn 页面链接到的// Javascript
String.prototype.luhnGet = function()
{
var luhnArr = [[0,1,2,3,4,5,6,7,8,9],[0,2,4,6,8,1,3,5,7,9]], sum = 0;
this.replace(/\D+/g,"").replace(/[\d]/g, function(c, p, o){
sum += luhnArr[ (o.length-p)&1 ][ parseInt(c,10) ]
});
return this + ((10 - sum%10)%10);
};
alert("54511187504546384725".luhnGet());
好
这个非常有用的EE4253页面验证了校验位,还显示了完整的计算和解释。
好
我需要 C# 代码,最终使用了这个code 项目代码:
// C#
public static int GetMod10Digit(string data)
{
int sum = 0;
bool odd = true;
for (int i = data.Length - 1; i >= 0; i--)
{
if (odd == true)
{
int tSum = Convert.ToInt32(data[i].ToString()) * 2;
if (tSum >= 10)
{
string tData = tSum.ToString();
tSum = Convert.ToInt32(tData[0].ToString()) + Convert.ToInt32(tData[1].ToString());
}
sum += tSum;
}
else
sum += Convert.ToInt32(data[i].ToString());
odd = !odd;
}
int result = (((sum / 10) + 1) * 10) - sum;
return result % 10;
}
好
这个 C# 中的验证代码function isLuhnValid($number)
{
if (empty($number))
return false;
$_j = 0;
$_base = str_split($number);
$_sum = array_pop($_base);
while (($_actual = array_pop($_base)) !== null) {
if ($_j % 2 == 0) {
$_actual *= 2;
if ($_actual > 9)
$_actual -= 9;
}
$_j++;
$_sum += $_actual;
}
return $_sum % 10 === 0;
}
/// <summary>
/// Calculates Luhn Check Digit based on
/// https://en.wikipedia.org/wiki/Luhn_algorithm
/// </summary>
/// <param name="digits">The digits EXCLUDING the check digit on the end.
/// The check digit should be compared against the result of this method.
/// </param>
/// <returns>The correct checkDigit</returns>
public static int CalculateLuhnCheckDigit(int[] digits)
{
int sum = 0;
bool isMultiplyByTwo = false;
//Start the summing going right to left
for (int index = digits.Length-1; index >= 0; --index)
{
int digit = digits[index];
//Every other digit should be multipled by two.
if (isMultiplyByTwo)
digit *= 2;
//When the digit becomes 2 digits (due to digit*2),
//we add the two digits together.
if (digit > 9)
digit = digit.ToString()
.Sum(character => (int)char.GetNumericValue(character));
sum += digit;
isMultiplyByTwo = !isMultiplyByTwo;
}
int remainder = sum % 10;
//If theres no remainder, the checkDigit is 0.
int checkDigit = 0;
//Otherwise, the checkDigit is the number that gets to the next 10
if (remainder != 0)
checkDigit = 10 - (sum % 10);
return checkDigit;
}
其使用示例:
public static bool IsValid(string userValue)
{
//Get the check digit from the end of the value
int checkDigit = (int)char.GetNumericValue(userValue[userValue.Length - 1]);
//Remove the checkDigit for the luhn calculation
userValue = userValue.Substring(0, userValue.Length - 1);
int[] userValueDigits = userValue.Select(ch => (int)char.GetNumericValue(ch))
.ToArray();
int originalLuhnDigit = CalculateLuhnCheckDigit(userValueDigits);
//If the user entered check digit matches the calcuated one,
//the number is valid.
return checkDigit == originalLuhnDigit;
}
试试这个:
<?php
function Luhn($digits) {
$sum = 0;
foreach (str_split(strrev($digits)) as $i => $digit) {
$sum += ($i % 2 == 0) ? array_sum(str_split($digit * 2)) : $digit;
}
return $digits . (10 - ($sum % 10)) % 10;
}
将 Luhn 校验和添加到 $input
$digits = Luhn($input);
使用 Luhn 校验和验证数字:
if ($digits == Luhn(substr($digits, 0, -1))) {
// ...
}
获取校验和号码:
$luhn_digit = substr(Luhn($digits), -1);
function isValdLuhn($str) {
$sum = 0;
$isSecond = false;
for ($i = strlen($str); $i >= 1; $i--) {
$num = $i - 1;
if($isSecond) {
$a = $str[$num] * 2;
$sum += intdiv($a, 10) + $a%10;
} else {
$sum += $str[$num];
}
$isSecond = !$isSecond;
}
return $sum % 10 === 0 ? 'VALID' : 'INVALID';
}
isValdLuhn('8174060176912597');