How to validate a standard EAN 13 barcode?

3

I would like to know how to validate a standard bar code EAN 13 like this 9788576356127 , this code has 13 digits and the criteria for validation are:

  • Perform the checker digit calculation;
  • Verify that the bar code has 13 digits;
  • I need to do this validations using object orientation by creating a validating class that has only one attribute called codigoBarra and a method called validar() that returns a string saying that it is a valid or invalid code, there may be other methods or attributes as well. How can I do it?

        
    asked by anonymous 05.03.2016 / 17:58

    4 answers

    6

    I found it interesting how Dener Carvalho's other authoring answer explains in detail the problem resolution.

    Problems

    However, the implementation that is provided is far from ideal. Some reasons:

  • Changeable object : This is bad practice. You pass the object to a routine and it may simply decide to change the bar code.
  • Object Orientation : The routine requires the creation of the object, but the computation of the result is done entirely apart from the state of the object. The validar method could be static or even receive a String and the main class would not even need to exist.
  • Efficiency : The routine performs several unnecessary operations, highlighting the loop that finds the check digit.
  • Inappropriate types : The validate method returns the result in String . While this may be convenient for display purposes, this is highly dangerous if placed within an actual application. At a minimum, constants or Enums should be used, but a boolean is more than enough in this case. The routine that prints on the screen or console should be responsible for generating the appropriate text so as not to mix the responsibilities.
  • One bar code to rule them all

    Thinking about an object model, we could introduce an abstract class thinking of variations in bar code type:

    public abstract class CodigoBarra {
    
        private final String codigoBarra;
        private final boolean valido;
    
        protected CodigoBarra(String codigoBarra) {
            if (codigoBarra == null) throw new IllegalArgumentException("Código não pode ser nulo");
            this.codigoBarra = codigoBarra;
            this.valido = validar(codigoBarra);
        }
    
        public String getCodigoBarra() {
            return codigoBarra;
        }
    
        public boolean valido() {
            return valido;
        }
    
        protected abstract boolean validar(String codigoBarra);
    
    }
    

    The idea is to have an abstract class that can represent any bar code.

    The state of this class includes the bar code itself and a flag telling whether the object is valid or not. The validation method is abstract and should be implemented by subclasses.

    Note that it is not allowed to create a barcode without specifying the code, although details of the validation are delegated to the abstract method.

    Implementation for EAN13

    Then the implementation for EAN:

    public class CodigoBarraEAN13 extends CodigoBarra {
    
        public CodigoBarraEAN13(String codigoBarra) {
            super(codigoBarra);
        }
    
        @Override
        protected boolean validar(String codigoBarra) {
            if (!codigoBarra.matches("^[0-9]{13}$")) {
                return false;
            }
            int[] numeros = codigoBarra.chars().map(Character::getNumericValue).toArray();
            int somaPares = numeros[1] + numeros[3] + numeros[5] + numeros[7] + numeros[9] + numeros[11];
            int somaImpares = numeros[0] + numeros[2] + numeros[4] + numeros[6] + numeros[8] + numeros[10];
            int resultado = somaImpares + somaPares * 3;
            int digitoVerificador = 10 - resultado % 10;
            return digitoVerificador == numeros[12];
        }
    
    }
    

    Notice how the validation algorithm was simple.

    The first if uses a regular expression to check if the barcode has exactly 13 numeric characters. We kill a lot of bunnies with a single paul, because from this point on we can assume several things about the content, such as the positions of the vector of numbers that we are going to generate next.

    Next, a vector of integers with the numbers of String is generated using a very practical and concise technique.

    Then the sums of even and odd indexes are made with hard-coded indexes. Using loops or streams is an example of unnecessary complexity. It just makes it look like the algorithm is more complicated than it really is.

    The result is calculated and the check digit can be obtained with a simple formula instead of using a loop.

    Using

    Usage is pretty much the same, let's see:

    String codigo = "9788576356127";
    CodigoBarra codigoBarra = new CodigoBarraEAN13(codigo);
    System.out.println("Número do código de barras: " + codigoBarra.getCodigoBarra());
    System.out.println("Código de barras é " + (codigoBarra.valido() ? "válido" : "inválido"));
    

    The difference is that the usage becomes more intuitive:

    • You do not need to pass the object to itself as in the other example (!)
    • Validation occurs only once in the life of the object
    • There is no risk of changing the object and thus affecting its state later
    • It's easier to check the status since we use a boolean, you do not have to go into Strings comparison questions, including details such as uppercase and lowercase
    • We can easily extend routines that use CodigoBarra to accept new types of bar codes.
    07.03.2016 / 04:17
    3

    EAN13 Bar Code

    The EAN13 barcode belongs to the GS1 System which is an official model for the standardization of product identification and commercial management processes, which has been in existence since 2006 and consists of a 13-digit sequence and its symbology represents the following items :

    • Identification of the product's country of origin.
    • Name of manufacturer.
    • Product identification number.
    • Type checker.

    See an image that illustrates its symbology:

    Calculation

    Oneofthemainrequirementstocheckifabarcodeisvalidistodotheverificationdigitcalculation,seehowthecalculationisdone:

      

    Supposeweareusingthebarcode:789162731405and  wewanttoknowwhatthefinaldigitis.(Verifier)

        

    Addallthedigitsofoddpositions(digits7,9,6,7,1and0):  7+9+6+7+1+0=30

        

    Addallthedigitsofevennumbers(digits8,1,2,3,4and5):  8+1+2+3+4+5=23

        

    Multiplythesumofthedigitsofevennumbersby3,see:23*3=69

        

    Addthetworesultsoftheprevioussteps,see:30+69=99

        

    Determinethenumberthatshouldbeaddedtothesumresultfor  ifyoucreateamultipleof10,see:99+1=100

        

    Therefore,thecheckdigitis1.

    Implementation

    UsingObjectOrientationIcreatedtheclassCodigoBarraEANresponsibleforbarcodevalidation,theclassdiagrambelowshowsitsstructure.

    DiagramofclassCodigoBarraEAN:

    TheclasshasonlythecodigoBarraattribute,itwillreceivethe13digitsofthebarcode.Theclasshastwoconstructorsoneemptyconstructorandtheotheronethatreceivesthebarcode.

    Explanationofmethods.

    Allmethodsbelowareresponsibleforbarcodevalidation,andonlyonemethodcanbeaccessedwhichisvalidar(),itreturnsastringsayingwhetherthebarcodeisvalidorinvalid.

    • MethodgetCodigoBarra()andsetCodigoBarra()public:providesaccesstothecodigoBarraattribute.
    • Public%method:isthemethodresponsibleforvalidatingthebarcode,itimplementstheotherprivatemethodsandcomparesthecheckerdigitwiththecheckerdigitreturnedbythecalculation,ifbothareequalthecodeisvalid.
    • Method%private%:getsthenumbersofthepositionsoddorevennumbersthatwillbeusedinthecalculation.
    • Methodvalidar()private:addsallthenumbersinalistoftypenumbersobterNumeroPosicao().
    • Method%withprivate%:removesthelastdigitofthebarcodethatisthescanningdigit.
    • Method%private%:obtainstheverifierdigitofthebarcode.
    • Method%withprivate%:checkstoseeifthebarcodeiswithintheEAN13standardcontainingthe13digits.
    • MethodsomarNumeros()private:calculatesandreturnsthebarcodescanner,tocalculatethedigitverifieritisnecessarytopassonlythe12digitswithoutthedigitchecker.

    FollowthewholecodeofclassList<Integer>below:

    packagecodigobarraeanverificador;importjava.util.ArrayList;importjava.util.List;/***@authorDener*/publicclassCodigoBarraEAN{privateStringcodigoBarra;publicCodigoBarraEAN(StringcodigoBarra){this.codigoBarra=codigoBarra;}publicCodigoBarraEAN(){}publicStringgetCodigoBarra(){returncodigoBarra;}publicvoidsetCodigoBarra(StringcodigoBarra){this.codigoBarra=codigoBarra;}//Métodosdeverificaçãoevalidaçãodocodigodebarras.publicStringvalidar(CodigoBarraEANcodigoBarra){Stringvalido;if(validarEAN(codigoBarra.getCodigoBarra())){intdigitoVerificador=obterDigitoVerificador(codigoBarra.getCodigoBarra());valido=(calcularDigitoVerificador(removerDigitoVerificador(codigoBarra.getCodigoBarra()))==digitoVerificador)?"OK" : "Inválido";
            }
            else
                valido = "Inválido";
    
            return valido;
        }
    
        private List<Integer> obterNumeroPosicao(String codigoBarra, String imparOuPar){        
            List<Integer> numeros = new ArrayList<>();
    
            for (int i = 0, posicao = 1; i < codigoBarra.length() - 1; i++){
                if ((posicao % 2 != 0))                        
                    numeros.add(Integer.parseInt(String.valueOf(codigoBarra.charAt(imparOuPar.equals("impar") ? posicao - 1 : posicao))));
    
                posicao++;
            }
    
            return numeros;
        }
    
        private int somarNumeros(List<Integer> numeros){
            return numeros.stream().reduce(0, Integer::sum);
        }
    
        private String removerDigitoVerificador(String codigoBarra){
            return codigoBarra.substring(0, codigoBarra.length() -1);
        }
    
        private int obterDigitoVerificador(String codigoBarra){
            return Integer.parseInt(String.valueOf(codigoBarra.charAt(codigoBarra.length() - 1)));
        }
    
        private boolean validarEAN(String codigoBarra){
            return (codigoBarra.length() == 13);
        }
    
        private int calcularDigitoVerificador(String codigoBarra){
            int somaPar = somarNumeros(obterNumeroPosicao(codigoBarra, "par")),
                somaImpar = somarNumeros(obterNumeroPosicao(codigoBarra, "impar"));        
            int multiplicaPar = somaPar * 3;        
            int resultado = somaImpar + multiplicaPar;
            int digitoVerificador = 0;
            int auxiliar = 0;        
    
            while ((resultado % 10) != 0){                        
                digitoVerificador++;
                resultado += digitoVerificador - auxiliar;
                auxiliar = digitoVerificador;
            }
    
            return digitoVerificador;
        }
    }
    

    Example use of class removerDigitoVerificador() :

    package codigobarraeanverificador;
    
    import java.util.Scanner;
    /**
     * @author Dener
     */
    public class CodigoBarraEANVerificador{
        public static void main(String[] args){
            System.out.println("Informa o código de barra: ");
            String codigo = new Scanner(System.in).nextLine();
    
            CodigoBarraEAN codigoBarra = new CodigoBarraEAN(codigo);
            System.out.println("Codigo de barra: " + codigoBarra.validar(codigoBarra));
            System.out.println("Numero do codigo de barras: " + codigoBarra.getCodigoBarra());
        }
    }
    

    Note:

      

    The class meets the requirements of the question, does not check the   source code and does not validate the product code or the   manufacturer.

    Fonts :
    Unraveling the mysteries of bar codes:
    link
    EAN-13:
    link

        
    06.03.2016 / 16:35
    1

    I was unable to find a barcode validator in Java, so I made a GS1 barcode validator, where it validates the GTIN-8, GTIN-12, GTIN-13, GTIN-14, GSIN and SSCC . It's a simple function where it returns true if the barcode is right.

    public boolean isValidarCodigoBarraGS1(String codigoBarras){
        boolean apenasNumeros = true;
    
        // Passa por todos os caracter checando se eh apenas numero
        for (char digito : codigoBarras.toCharArray()) {
            // Checa se eh um numero
            if (!Character.isDigit(digito)) {
                apenasNumeros = false;
                break;
            }
        }
        // Checa se o que foi passado por parametro eh apenas numero
        if (apenasNumeros){
            // Salva o ultimo digito do codigo (digito verificador)
            int digito = Integer.parseInt(""+codigoBarras.charAt(codigoBarras.length() -1));
            // Pega todos os digetos mas sem o digito verificador
            String codigoSemDigitoVerificador = codigoBarras.substring(0, codigoBarras.length() -1);
            // Vareavel para armazenar a soma dos digitos
            int soma = 0;
    
            // Checa a quantidade de digito
            if ( (codigoBarras.length() == 13) || (codigoBarras.length() == 17) ){
    
                // Passa por todos os digitos do codigo sem o digito verificador
                for(int i = 0; i < codigoSemDigitoVerificador.length(); i++){
                    // Checa se i eh par
                    if (( (i+1) % 2) == 0) {
                        // Mutiplica por 3 e depois soma
                        soma += Integer.parseInt(""+codigoSemDigitoVerificador.charAt(i)) * 3;
                    } else {
                        // Soma os digitos
                        soma += Integer.parseInt(""+codigoSemDigitoVerificador.charAt(i));
                    }
                }
            } else if ( (codigoBarras.length() == 8) || (codigoBarras.length() == 12) || (codigoBarras.length() == 14) || (codigoBarras.length() == 18) ){
                // Passa por todos os digitos do codigo sem o digito verificador
                for(int i = 0; i < codigoSemDigitoVerificador.length(); i++){
    
                    // Checa se i eh par
                    if (( (i+1) % 2) == 0) {
                        // Soma os digitos
                        soma += Integer.parseInt(""+codigoSemDigitoVerificador.charAt(i));
                    } else {
                        // Mutiplica por 3 e depois soma
                        soma += Integer.parseInt(""+codigoSemDigitoVerificador.charAt(i)) * 3;
                    }
                }
            // Retorna falso caso nao seja um codigo de barra do tamanho valido pelo GS1
            } else {
                return false;
            }
            int somaMultipla = soma;
    
            // Entra no while enquanto a soma nao for multiplo de 10
            while ( (somaMultipla % 10) != 0 ){
                somaMultipla ++;
            }
            // Subtraia soma por um múltiplo de 10 superior mais próximo a ele
            // Depois checa se o resultado da subtracao eh igual ao digito passado por paramento
            return (soma - somaMultipla) == digito;
    
        } else {
            return false;
        }
    }
    
        
    31.05.2016 / 01:23
    1

    I know this is an old question, but I had a similar problem recently and had to implement a proprietary barcode validation solution (EAN-13 and EAN-8).

    Today's own Apache Commons library EAN13CheckDigit offers a method for calculating verifying digits and validating bar codes.

    However, I have observed several situations where only calculating check digit and validating that the length of the EAN is equal to 13 (or 8) does not perform a valid validation. Often I encountered invalid bar codes that completed the required number of digits with leading zeros and coincidentally generated valid check digits.

    By the definition of the bar code system, it is also known that its first digits indicate the numerical system to which it represents. This number can specify the country of manufacture of the product or even indicate its category. In this situation we can have one to two zeros left to indicate a particular system or country.

    Thinking through these questions, I developed the following code. The EANValidationUtil class has the isValid method that can receive Long or String values. First the input parameter is handled to avoid possible errors. Then, leading zeroes are removed from this value and the length of the bar code is validated. Finally, the calculation of the check digit is performed.

    Here is the proposed code:

    import lombok.experimental.UtilityClass;
    import lombok.val;
    
    @UtilityClass
    public class EANValidationUtil {
    
        public static boolean isValid(final String ean) {
            boolean status = false;
            if (!ean.isEmpty()) {
                // Remove nom numeric characters
                String treatedEanStr = ean.replaceAll("[^\d.]", "");
                // Remove leading zeros
                String shortenedEanStr = treatedEanStr.replaceFirst("^0+(?!$)", "");
                // Validate length
                if (shortenedEanStr.length() > 7 && shortenedEanStr.length() < 14) {
                    int sum = 0;
                    val digits = treatedEanStr.split("");
                    for (int i = 0; i < (digits.length - 1); i++) {
                        sum += Integer.parseInt(digits[i]) * ((i % 2 == 0) ? 1 : 3);
                    }
                    int checksumDigit = (10 - (sum % 10)) % 10;
                    status = (Integer.parseInt(digits[digits.length - 1]) == checksumDigit);
                }
            }
            return status;
        }
    
        public static boolean isValid(final Long ean) {
            return isValid(ean.toString());
        }
    }
    
        
    29.08.2017 / 16:41