C:信用卡号码检查器/ Luhn 算法

Posted

技术标签:

【中文标题】C:信用卡号码检查器/ Luhn 算法【英文标题】:C: Credit Card Number checker/ Luhn's algorithm 【发布时间】:2012-02-29 11:48:39 【问题描述】:

一切看起来都很好,似乎遵循 Luhn 的算法,但是当我输入我自己的信用卡号或这个应该有效的样本号: 4388576018410707 时,它仍然返回无效......

谁能找到问题?

#include <stdio.h>

int isvalid(long num);
int sumofdoubleevenplace(long num);
int getdigit(int num);
int sumofoddplace(long num);
int prefixmatched(long num,int d);
int getsize(long d);
int getprefix(long num,int k);

main()
  long cardnum=0;
  printf("Enter credit card number ");
  scanf("%ld",&cardnum);
  if(isvalid(cardnum)==1)
    printf("Valid card number\n");
else
    printf("Invalid card number\n ");
return 0;


int isvalid(long num)
if(((sumofoddplace(num)+sumofdoubleevenplace(num))%10==0)
   && (getsize(num)<=16 && getsize(num)>=13)
   && (prefixmatched(num,4)==1 || prefixmatched(num,5)==1 ||
       prefixmatched(num,6)==1 || prefixmatched(num,37==1)))
    return 1;
else
    return 0;


int sumofdoubleevenplace(long num)
int numdigits=getsize(num)-1;
int sum=0,i;
num/=10;
for(i=0;i<numdigits;i+=2)
    sum+=getdigit((int)(2*(num % 10)));
    num/=100;

return sum;


int getdigit(int num)
return ((num-num%10)/10)+num%10;


int sumofoddplace(long num)
int numberofdigits=getsize(num);
int sum=0,i;
for(i=0;i<numberofdigits;i+=2)
    sum+=num%10;
    num/=100;

return sum;


int prefixmatched(long num,int d)
if(getprefix(num,getsize(d))==d)
    return 1;
else
    return 0;


int getsize(long d)
int n=0;
while(d!=0)
    d/=10;
    n++;

return n;


int getprefix(long num,int k)
int numberofdigits=getsize(num);
int i;
if(numberofdigits-k>0)
    for(i=0;i<numberofdigits-k;i++)
        num/=10;
    
    return num;

else
    return num;

【问题讨论】:

该代码适用于我,您提供的卡号和我自己的信用卡。 无论我输入什么,它都会返回“无效数字”。这可能是编译器特定的东西吗? 有可能。有没有跟进用户 Pubby 的建议,检查 long 的 sizeof 大小? long 的大小为 4 字节。这是什么意思? 因为您的代码不可移植 - 它假设 long 是 64 位,但它在您的系统上是 32 位。使用正确的类型:uint64_t 【参考方案1】:

由于long只有4个字节,所以最多只能存储2,147,483,648

您需要存储 4,388,576,018,410,707 显然无法容纳。使用 64 位整数,例如 uint64_t

【讨论】:

“uint64_t”在哪个标头下?【参考方案2】:

首先要做的是打印你读到的数据;你试过吗?使用 64 字节整数,您可以处理 16 位卡号,但输入格式必须相当严格。或者,您可以将数字读取为字符串(这意味着您的程序可以允许可选的标点符号;我发现网站不允许您在实际信用卡上的数字分组时键入空格或破折号非常令人讨厌)。调试问题时,检查程序实际使用的输入数据是否与您的预期一致。

如果您使用的是 32 位编译器,就会遇到问题!

在 64 位模式下编译您的代码后,示例信用卡号被识别为有效。

当您的代码以 32 位模式编译时,示例信用卡号被识别为无效。 (上面有一条注释,sizeof(long) == 4 在您的机器上使用您的编译器。您正在以 32 位模式编译,或者您正在 Windows 64 位平台上以 64 位模式编译。请参阅:What is the bit size of long on 64-bit Windows?)

当我使用我十多年前编写的 Perl 脚本时,样本 CCN 被识别为有效。

当我打印出 scanf() 在 32 位模式下(你的程序的修改版本)读取的值时,我得到:

$ ./ccn <<< 4388576018410707
Enter credit card number Invalid card number -0000000089805613
$

这是使用bash 特有的功能将字符串(数字)作为标准输入提供给程序。

经验教训

    了解您的计算机可以处理哪些数字。

    检查scanf()的返回值。

    但是,即使这样也无助于溢出。您最好将一行文本读入一个字符串,然后使用strtol() 或亲戚来检查转换。小心,您可以发现溢出和下溢以及无效值等。并且您可以报告用户输入的内容,而对于失败的数字转换(或虚假的数字转换),您无法报告程序看到的内容.

    打印输入数据。如果您在输入正数时在输出中看到负数,您会立即知道为什么会出现问题。

    如果它是我的程序,它将处理命令行参数而不是提示输入。


Perl 示例:

$ perl -MBRPS -e 'my($x, $y) = validate_account("4388-5760-1841-0708"); print "$x : $y\n";'
 : check digit on account number is incorrect
$ perl -MBRPS -e 'my($x, $y) = validate_account("4388-5760-1841-0707"); print "$x : $y\n";'
4388576018410707 : ok
$ perl -MBRPS -e 'my($x, $y) = validate_account("4388576018410707"); print "$x : $y\n";'
4388576018410707 : ok
$ perl -MBRPS -e 'my($x, $y) = validate_account("4388 5760 1841 0707"); print "$x : $y\n";'
4388576018410707 : ok
$ perl -MBRPS -e 'my($x, $y) = validate_account("4388 57601841 0707"); print "$x : $y\n";'
 : invalid punctuation pattern
$

这区分了用户友好的表示格式,允许空格或破折号分隔数字组,以及内部操作格式,计算机可以处理的数字字符串。不应该要求人们输入没有标点符号的 16 位数字;那是彻头彻尾的不文明(尽管我遇到的每个网站都坚持不使用标点符号)。你可以很容易地编写一个函数来格式化一个带中断的 16 位数字。

上面的函数不是为了向用户提供数据——它确实识别出了问题所在,但程序员必须决定如何处理错误。

【讨论】:

【参考方案3】:

您可以使用 int long long。至少能够包含 [−9223372036854775807, +9223372036854775807] 范围

根据C99 标准,long long 是至少 64 位宽的整数类型。指定了两种整数 64 位类型:long long intunsigned long long int

【讨论】:

以上是关于C:信用卡号码检查器/ Luhn 算法的主要内容,如果未能解决你的问题,请参考以下文章

程序冻结 - Luhn 算法

当我们使用 Luhn 算法验证信用卡时,为啥要反转数字?

LUHN 信用卡验证在有效卡号上失败

如何使用 Luhn 算法创建信用卡验证器?

Java中的Luhn公式实现

为啥Luhn算法乘以2?