Boucle dans la validation de carte de crédit en java


Je suis lycéen dans un cours d'introduction à l'informatique. Notre mission était la suivante:

Le dernier chiffre d'un numéro de carte de crédit est le chiffre de contrôle, qui protège contre les erreurs de transcription telles qu'une erreur dans un seul chiffre ou une commutation de deux chiffres. la méthode suivante est utilisée pour vérifier les numéros de carte de crédit réels mais, pour plus de simplicité, nous la décrirons pour les numéros à 8 chiffres au lieu de 16:

  • À partir du chiffre le plus à droite, formez somme de tous les autres chiffres. Par exemple, si le numéro de carte de crédit est 4358 9795, alors vous formez la somme 5+7+8+3 = 23.
  • Doubler chacun des chiffres qui n'étaient pas inclus dans l'étape précédente. Ajoutez tous les chiffres des nombres résultants. Par exemple, avec les nombres donnés ci-dessus, doubler les chiffres, en commençant par l'avant-dernier, donne 18 18 10 8. L'ajout de tous les chiffres dans ces valeurs de rendements 1+8+1+8+1+0+8=27.
  • Ajouter les sommes des deux étapes précédentes. Si le dernier chiffre de le résultat est 0, le nombre est valide. Dans notre cas, 23 + 27 = 50, donc le nombre est valide.

Écrivez un programme qui implémente cet algorithme. L'utilisateur doit fournir un numéro à 8 chiffres et vous devez imprimer si le numéro est valide ou non. S'il n'est pas valide, vous devez imprimer la valeur du chiffre de contrôle qui rendrait le nombre valide.

J'ai tout fait sauf la partie en gras. Mon code est répertorié ci-dessous:

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;
    }
}

La boucle est ce que je suis d'avoir des problèmes avec. Je me retrouve toujours dans une boucle infinie chaque fois qu'il compile. Il semble que tout devrait fonctionner, cependant. Il est censé diminuer la valeur du chiffre de contrôle (pas augmenter donc je ne me retrouve pas avec un chiffre de contrôle de 10 ou plus), puis ajouter ce nombre dans la somme totale jusqu'à ce que la somme totale soit divisible par 10, puis la boucle se terminerait. Le type de boucle que j'utilise est-il incorrect? Tout conseil serait apprécié.

Author: Hovercraft Full Of Eels, 2013-11-24

4 answers

Votre problème est que vos deux conditions de boucle impliquent totalSum mais vous ne changez que checkDigit.

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

Vous devez recalculer totalSum ou modifier la condition pour être basée sur checkDigit. Si vous voulez boucler et décrémenter comme vous le faites, vous devrez ajouter une méthode qui exécute l'algorithme et l'appeler à chaque fois. La façon dont vous avez décrit votre classe rend cela très gênant car vous ne convertissez pas les nombres.

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;
}

Maintenant, vous pouvez faire quelque chose comme:

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];
}

Mais vous devriez pouvoir simplement soustraire la différence pour trouver le chiffre de contrôle valide:

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

Ou quelque chose comme:

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

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

    valid = true;
}

Quelques apartés,

Je recommanderais de stocker les chiffres en tant que champ et de faire en sorte que checkDigit représente un index dans le tableau. Cela simplifierait certaines des opérations que vous effectuez.

Je suggère également de ne pas changer" silencieusement " les champs en interne, c'est-à-dire comme dans votre méthode makeItValid, sauf s'il s'agit d'une spécification de affectation. Je pense qu'une meilleure forme est de laisser le code "propriétaire" faire les changements lui-même, ce qui est plus clair à l'extérieur. Une implémentation quelque peu complète ressemblerait à ceci:

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
}

La meilleure forme de l'OMI serait de refuser la création d'un objet de carte de crédit à moins que le chiffre de contrôle ne soit valide. En tant que principe de POO, il ne devrait pas avoir de sens de créer des cartes de crédit invalides. Le constructeur doit lever une exception si la carte n'est pas valide et avoir une méthode statique pour corriger le nombre.

Je ferais quelque chose comme ce qui suit (raccourci):

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);
}

Je suppose que c'est plus ou moins faire vos devoirs pour vous, mais je suis sûr que vous pouvez en tirer des leçons.

 2
Author: Radiodef, 2013-11-24 02:00:54

À moins que je manque quelque chose de majeur sur le fonctionnement de la validation, votre méthode makeitvalid ne fonctionnera pas de la manière dont vous l'abordez.

Il est plus logique (du moins pour moi) d'extraire tout ce que vous avez dans votre constructeur dans une méthode ie.

boolean isValid(String cardNumber);

Qui ferait tout ce que votre constructeur fait sauf définir l'indicateur valide. votre constructeur devient alors

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

Et puis pour trouver quel changement le rendrait valide votre méthode check valid fait quelque chose comme

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.
 0
Author: thermite, 2013-11-23 23:39:32

Quelque chose dans ce sens devrait faire. Vous devrez toujours implémenter quelques méthodes par vous-même.

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);
}
 0
Author: Lopina, 2013-11-24 00:55:13

Ce programme est très dynamique. Je n'ai pas ajouté trop de gestion des erreurs. Vous pouvez entrer n'importe quel nombre divisible par 8.

Code:

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);
    }
}

J'ai écrit un utilitaire de sommation de chiffres séparé:

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));
        }
    }
}
 0
Author: Mr. Polywhirl, 2013-11-24 01:43:52