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

时间:2012-02-08 01:25:38

标签: c credit-card luhn

一切看起来都很好,似乎遵循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;
}

3 个答案:

答案 0 :(得分:4)

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

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

  • 使用64位模式编译代码时,样本信用卡号被识别为有效。

  • 使用32位模式编译代码时,样本信用卡号被识别为无效。 (在您的计算机上有一个带有编译器sizeof(long) == 4的注释。要么是在32位模式下编译,要么在Windows 64位平台上以64位模式编译。请参阅:{{ 3}})

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

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

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

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

带走的教训

  1. 了解您的计算机可以通过数字方式处理的内容。

  2. 检查scanf()的返回值。

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

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

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


  6. 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位数字。

    上述功能并非旨在向用户呈现数据 - 它确实识别出问题所在,但程序员必须决定如何处理错误。

答案 1 :(得分:1)

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

你需要存储4,388,576,018,410,707,这显然是不适合的。使用像uint64_t这样的64位整数。

答案 2 :(得分:0)

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

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