2

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

3 回答 3

4

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

如果您使用的是 32 位编译,那么您就有问题了!

  • 以 64 位模式编译代码后,示例信用卡号被识别为有效。

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

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

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

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

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

要带走的教训

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

  2. 检查来自的返回值scanf()

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

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

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


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

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

于 2012-02-08T04:51:54.363 回答
2

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

您需要存储 4,388,576,018,410,707 显然不适合。使用 64 位 int,例如uint64_t.

于 2012-02-08T03:47:23.563 回答
0

You can use the int long long. Capable of containing at least the [−9223372036854775807, +9223372036854775807] range

According to C99 standard, long long is an integer type which is at least 64-bit wide. There are two integer 64-bit types specified: long long int and unsigned long long int

于 2016-02-24T11:26:44.227 回答