【问题标题】:Loop in Credit Card Validation in javajava中的信用卡验证循环
【发布时间】:2013-12-08 18:13:15
【问题描述】:

我是一名计算机科学入门课程的高中生。我们的任务如下:

信用卡号的最后一位是校验位,可防止抄录错误,例如一位数错误或两位数切换。以下方法用于验证实际的信用卡号码,但为简单起见,我们将使用 8 位数字而不是 16 位数字来描述它:

  • 从最右边的数字开始,形成所有其他数字的总和。例如,如果信用卡号是 4358 9795,那么您的和就是 5+7+8+3 = 23。
  • 将前面步骤中未包含的每个数字加倍。添加结果数字的所有数字。例如,对于上面给出的数字,将数字加倍,从倒数第二个开始,得到 18 18 10 8。将这些值中的所有数字相加得到 1+8+1+8+1+0+8 =27。
  • 将前面两个步骤的总和相加。如果结果的最后一位为 0,则该数字有效。在我们的例子中,23 + 27 = 50,所以这个数字是有效的。

编写一个程序来实现这个算法。用户应提供一个 8 位数字,您应打印出该数字是否有效。 如果它无效,您应该打印出使数字有效的校验位的值

除了粗体部分之外,我已经完成了所有工作。我的代码如下:

public class CreditCard 
{ 

    private String creditCardNumber;
    private boolean valid;
    private int checkDigit;
    int totalSum;

    /**
     * Constructor for objects of class CreditCard
     */
    public CreditCard(String pCreditCardNumber)
    {
        creditCardNumber = pCreditCardNumber;
        checkDigit = Integer.parseInt(pCreditCardNumber.substring(creditCardNumber.length() - 1));
        int sumOfDigits = checkDigit + Integer.parseInt(pCreditCardNumber.substring(6,7)) + Integer.parseInt(pCreditCardNumber.substring(3,4)) + Integer.parseInt(pCreditCardNumber.substring(1,2));
        int dig7 = Integer.parseInt(pCreditCardNumber.substring(7,8));
        int dig5 = Integer.parseInt(pCreditCardNumber.substring(5,6));
        int dig3 = Integer.parseInt(pCreditCardNumber.substring(2,3));
        int dig1 = Integer.parseInt(pCreditCardNumber.substring(0,1));

        String string7 = Integer.toString(dig7);
        int doubledDig7a = Integer.parseInt(string7.substring(0));
        int doubledDig7b = 0;
        if (dig7 * 2 >= 10)

        {
            doubledDig7a = Integer.parseInt(string7.substring(0));
            doubledDig7b = 0;
        }

        String string5 = Integer.toString(dig5);
        int doubledDig5a = Integer.parseInt(string7.substring(0));
        int doubledDig5b = 0;
        if (dig5 * 2 >= 10)

        {
            doubledDig5a = Integer.parseInt(string5.substring(0));
            doubledDig5b = 0;
        }

        String string3 = Integer.toString(dig3);
        int doubledDig3a = Integer.parseInt(string3.substring(0));
        int doubledDig3b = 0;
        if (dig3 * 2 >= 10)

        {
            doubledDig3a = Integer.parseInt(string3.substring(0));
            doubledDig3b = 0;
        }

        String string1 = Integer.toString(dig1);
        int doubledDig1a = Integer.parseInt(string1.substring(0));
        int doubledDig1b = 0;
        if (dig1 * 2 >= 10)

        {
            doubledDig1a = Integer.parseInt(string1.substring(0));
            doubledDig1b = 0;
        }


        int doubleDigits = doubledDig1a + doubledDig1b + doubledDig3a + doubledDig3b + doubledDig5a + doubledDig5b + doubledDig7a + doubledDig7b;

        totalSum = sumOfDigits + doubleDigits;

        if (totalSum % 10 == 0)
        {
            valid = true;
        }
        else
        {
            valid = false;
        }

    }

    public void makeItValid()
    {
       while (totalSum % 10 != 0)
       {
           checkDigit--;
           if (totalSum % 10 == 0)
           {
               break;
            }
        }
    }


    public boolean isItValid()
    {
        return valid;
    }
}

循环是我遇到的问题。每当它编译时,我总是会陷入无限循环。不过,看起来一切都应该正常工作。它应该减少校验位的值(不增加,所以我最终不会得到 10 或更高的校验位),然后将该数字加回总和,直到总和可被 10 整除,并且然后循环将结束。我使用的循环类型是否错误?任何意见,将不胜感激。

【问题讨论】:

  • 只是一个评论:你的代码比你需要的多 100 倍
  • 我知道这不适用于你的作业,但这里有一些你需要知道的。您从不输入任何银行号码或密码作为字符串。字符串被扔进一个池中,所以它们不会被垃圾收集器收集。如果你要在现实世界中这样做,黑客可以很容易地获取卡号。相反,您应该使用字符数组。您可以将所有字符重置为零,但您不能对字符串执行此操作,因为它们是不可变的。这只是一个提示。 TLDR: 切勿将字符串用于敏感数字。
  • @Mr.Polywhirl : 1. 并不是所有的字符串都会被自动保留,在这种情况下信用卡号不会是 - 请参阅String.intern() 文档。 2. 如果黑客可以“非常轻松”地访问您的虚拟机内存,那么您还有其他问题。

标签: java loops if-statement


【解决方案1】:

您的问题是您的两个循环条件都涉及totalSum,但您只更改checkDigit

while (totalSum % 10 != 0)
{
    checkDigit--;
    if (totalSum % 10 == 0)
    {
        break;
    }
}

您要么需要重新计算totalSum,要么将条件更改为基于checkDigit。如果您想像您正在做的那样循环和递减,您将需要添加一个执行算法并每次调用它的方法。您对班级进行概述的方式使这非常不方便,因为您不转换数字。

public static int[] cardToNumbers(String cardText) {

    // \D is regex for non-digits
    cardText = cardText.replaceAll("\\D", "");

    int[] cardNumbers = new int[cardText.length()];

    // convert unicode to corresponding integers
    for (int i = 0; i < cardText.length(); i++)
        cardNumbers[i] = cardText.charAt(i) - '0';

    return cardNumbers;
}

public static int calcTotalSum(int[] cardNumbers) {

    int sum = 0;

    /* "every other one" loops
     *
     * I recommend against the "mod 2 index" scheme
     * i % 2 relies on the card number being even
     * you can't have your code blow up with unusual inputs
     *
     */

    for (int i = cardNumbers.length - 1; i >= 0; i -= 2) {
        sum += cardNumbers[i];
    }
    for (int i = cardNumbers.length - 2; i >= 0; i -= 2) {
        int dig = cardNumbers[i] * 2;
        while (dig > 0) {
            sum += dig % 10;
            dig /= 10;
        }
    }

    return sum;
}

现在您可以执行以下操作:

public void makeItValid() {
    int[] invalidNumbers = cardToNumbers(creditCardNumber);

    int sum = calcTotalSum(invalidNumbers);

    while ((sum = calcTotalSum(invalidNumbers)) % 10 != 0)
        invalidNumbers[invalidNumbers.length - 1]--;

    totalSum = sum;
    checkDigit = invalidNumbers[invalidNumbers.length - 1];
}

但是你应该可以只减去差来找到有效的校验位:

if (totalSum % 10 != 0) checkDigit -= totalSum % 10;

或者类似的东西:

public void makeItValid() {
    int[] invalidNumbers = cardToNumbers(creditCardNumber);

    checkDigit = invalidNumbers[invalidNumbers.length - 1] -= totalSum % 10;
    totalSum = calcTotalSum(invalidNumbers);

    valid = true;
}

一些旁白,

我建议将数字存储为一个字段,并让checkDigit 代表数组中的一个索引。这将简化您正在执行的一些操作。

我还建议不要像在 makeItValid 方法中那样在 IE 内部“默默地”更改字段,除非这是分配的规范。我认为更好的形式是让“拥有”代码自己进行更改,这在外部更加清晰。一个比较完整的实现应该是这样的:

public class CreditCard {
    public static void main(String[] args) {
        if (args.length == 0) return;

        CreditCard card = new CreditCard(args[0]);

        if (!card.isValidNumber()) {
            card.setCheckDigit(card.getValidCheckDigit());
        }
    }

    private final String cardText;
    private final int[] cardDigits;
    private final int cdIndex;

    public CreditCard(String ct) {
        cardDigits = cardToNumbers(cardText = ct);

        if ((cdIndex = cardDigits.length - 1) < 0) {
            throw new IllegalArgumentException("# had no digits");
        }
    }

    public boolean isValidNumber() {
        return calcTotalSum(cardDigits) % 10 == 0;
    }

    public void setCheckDigit(int dig) {
        cardDigits[cdIndex] = dig;
    }

    public int getValidCheckDigit() {
        int sum = calcTotalSum(cardDigits);
        if (sum % 10 != 0) {
            return cardNumbers[cdIndex] - sum % 10;
        } else {
            return cardNumbers[cdIndex];
        }
    }

    // above static methods
}

IMO 的最佳形式是根本不允许创建信用卡对象,除非校验位有效。作为 OOP 原则,创建无效信用卡应该没有意义。如果卡片无效并且有一个静态方法来更正数字,构造函数应该抛出异常。

我会做类似以下的事情(缩短):

public class CreditCard {
    public CreditCard(String number) {
        if (!validateCheckDigit(number)) {
            throw new IllegalArgumentException("check digit failure");
        }
    }
}

public static void main(String[] args) {
    String number = args[0];
    CreditCard card = null;

    boolean valid = false;
    do {
        try {
            card = new CreditCard(number);
            valid = true;
        } catch (IllegalArgumentException e) {
            number = CreditCard.correctCheckDigit(number);
        }
    } while (!valid);
}

我想这或多或少是在为你做功课,但我相信你可以从中学习。

【讨论】:

  • 当我执行 if 语句时,totalSum 的值不会改变。不应该因为校验位比原来的小而改变吗?
  • 查看我更新的编辑。如果要更改总和,则需要重新计算总和。我还强烈建议您查看您的类的布局,A)为卡号添加一个字段作为整数,B)创建一个方法,该方法是您所描述的算法的实现。
  • 感谢您付出的所有努力。不幸的是,我们还没有了解您代码中的一半内容,所以我真的不知道从哪里开始。
  • 呃,您的calcTotalSum() 方法正确吗?我认为OP需要将每个偶数位置数字加倍,然后总结所述产品的数字。请参阅我的answer 进行澄清。
  • 不客气。还有哪些部分你不明白?如果您不允许使用该课程尚未涵盖的概念,我建议您只寻找您确实理解的东西。例如,如果您不知道如何使用数组,则可以直接从字符串计算总和。 Integer.parseInt 也是一种将字符串转换为整数的完全有效的方法。 @Mr.Polywhirl 你说得对,我看错了,我已经更正了,谢谢。
【解决方案2】:

除非我遗漏了有关验证工作原理的重要内容,否则您的 makeitvalid 方法不会以您接近它的方式工作。

将构造函数中的所有内容提取到方法中更有意义(至少对我而言)。

boolean isValid(String cardNumber);

除了设置有效标志外,它会做你的构造函数所做的一切。然后你的构造函数变成

public CreditCard(String pCreditCardNumber){
    valid = isValid(pCreditCardNumber);
}

然后找到哪些更改会使其有效,您的检查有效方法会执行类似的操作

change the value of check digit
   if (isValid(Changed String))
        return checkdigit
   else
       continue
repeat until you either find one that works or until you determine that it can't work.

【讨论】:

    【解决方案3】:

    应该做一些类似的事情。您仍然需要自己实现一些方法。

    public static void main(String[] args) {
        String creditCardNumber = readCreditCardNumber();
        String correctCreditCardNumber = getCorrectCreditCardNumber(creditCardNumber);
    
        if (creditCardNumber.equals(correctCreditCardNumber)) {
            System.out.println("Credit Card Valid");
        } else {
            System.out.println("Credit Card Invalid. Did you mean " + correctCreditCardNumber + "?");
        }
    }
    
    public static String getCorrectCreditCardNumber(String creditCardNumber) {
        int[] creditCardDigits = getCreditCardDigits(creditCardNumber);
    
        int sum = 0;
        for (int i = creditCardDigits.length - 2; i >= 0; i--) {
            if (isOdd(i)) {
                sum += creditCardDigits[i];
            } else {
                sum += digitSum(creditCardDigits[i] * 2);
            }
        }
    
        int last = creditCardDigits.length - 1;
        int remainder = sum % 10;
        if (remainder != 0) {
            creditCardDigits[last] = 10 - remainder;
        }
    
        return getCreditCardNumberAsString(creditCardDigits);
    }
    

    【讨论】:

      【解决方案4】:

      这个节目非常有活力。我没有添加太多错误处理。您可以输入任何能被 8 整除的数字。

      实际代码:

      Enter a card number: 4358 9795
      Number is valid?: true
      
      Continue? (y/n): y
      
      Enter a card number: 4358 9796
      Number is valid?: false
      
      Continue? (y/n): y
      
      Enter a card number: 43-58 97-95
      Number is valid?: true
      
      Continue? (y/n): n
      
      Exiting...
      

      CreditCardValidator.java

      import java.text.ParseException;
      import java.util.Scanner;
      
      public class CreditCardValidator {
          Integer[] digits;
      
          public CreditCardValidator(String numberSequence) {
              parseNumber(numberSequence);
          }
      
          private void parseNumber(String numberSequence) {
              try {
                  String sequence = numberSequence.replaceAll("[\\s-]+", "");
      
                  int length = sequence.length();
      
                  if (length % 8 != 0) {
                      throw new IllegalArgumentException("Number length invalid.");
                  }
      
                  digits = new Integer[length];
      
                  int pos = 0;
                  for (Character c : sequence.toCharArray()) {
                      if (Character.isDigit(c)) {
                          digits[pos++] = Character.getNumericValue(c);
                      } else {
                          throw new ParseException("Invalid digit.", pos);
                      }
                  }
              } catch (Exception e) {
                  e.printStackTrace();
              }
          }
      
          private boolean validateNumber() {
              int sum = 0;
      
              for (int i = digits.length - 1; i >= 0; i--) {
                  if (i % 2 == 1) {
                      sum += digits[i];
                  } else {
                      sum += NumberUtils.sumDigits(digits[i] * 2);
                  }
              }
      
              return sum % 10 == 0;
          }
      
          public static void main(String[] args) {
              boolean stop = false;
              CreditCardValidator c;
      
              while (!stop) {
                  System.out.print("Enter a card number: ");
                  c = new CreditCardValidator(new Scanner(System.in).nextLine());
                  System.out.println("Number is valid?: " + c.validateNumber());
                  System.out.print("\nContinue? (y/n): ");
      
                  if (new Scanner(System.in).next().charAt(0) == 'n') {
                      stop = true;
                  }
      
                  System.out.println();
              }
      
              System.out.println("Exiting...");
              System.exit(0);
          }
      }
      

      我写了一个单独的数字求和实用程序:

      public class NumberUtils {
          public static void main(String[] args) {
              for(int i = 0; i < 2000; i+=75) {
                  System.out.printf("%04d: %02d\n", i, sumDigits(i));
              }
          }
      
          public static int sumDigits(int n) {
              if (n < 0)
                  return 0;
      
              return sumDigitsRecursive(n, 0);
          }
      
          private static int sumDigitsRecursive(int n, int total) {
              if (n < 10)
                  return total + n;
              else {
                  return sumDigitsRecursive(n / 10, total + (n % 10));
              }
          }
      }
      

      【讨论】:

        猜你喜欢
        • 2013-10-30
        • 2014-08-14
        • 1970-01-01
        • 2011-01-24
        • 2012-08-14
        • 2017-04-02
        • 2016-11-15
        • 2020-11-11
        • 1970-01-01
        相关资源
        最近更新 更多