How to validate a standard EAN 13 barcode?

Asked

Viewed 11,902 times

3

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

  1. Perform the check digit calculation;
  2. Check if the current checker digit matches the calculation result (which in this case is the calculated checker digit);
  3. Check if the barcode has 13 digits;

I need to do this validations using object orientation by creating a class responsible for validation that has only one attribute called codigoBarra and a method called validar() that returns a string saying whether it is valid or invalid code, there may be other methods or attributes as well if needed. How can I do?

  • That method of Validation doesn’t suit you?

  • @Marcelodeandrade used the method of this link but performs the wrong calculation.

  • @Bacco is for college, must create a class that makes the validation, do not worry I’ll answer because I have already solved here. I will edit the question and specify it better in detail.

  • @It’s just, as you put it in the question, I was wondering what OO was going to do to help with the EAN problem. It’s just that I don’t know. If you solved it, it’s good to post, in case someone else needs it.

  • @Bacco tranquilow :) So I reply you can help me by improving the code or giving an answer if you want :D.

  • @I denigrate how I do not use Java, I just invented not responding, but after you post, if there is something I can opine on, I comment. I’m finishing a bit here, then I do a little research on the EAN calculation.

  • 1

    @Bacco answered the question.

Show 2 more comments

4 answers

6


I found interesting how the other answer by Dener Carvalho explains in detail the resolution of the problem.

Problems

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

  1. Changeable object: is a bad practice. You pass the object to a routine and it can simply decide to change the barcode.
  2. 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 method validar could be static or even receive a String and the main class would not even need to exist.
  3. Efficiency: routine performs various unnecessary operations, highlighting the loop you find of the checker digit.
  4. Unsuitable types: the validar method returns the result in a String. While this may be convenient for display purposes, it is highly dangerous if placed within a real application. Constants or Enums should at least be used, but a Boolean is more than enough in this case. The routine you print on the screen or console should be responsible for generating the appropriate text so as not to mix responsibilities.

One bar code to Rule them all

Thinking about an object model, we could introduce an abstract class thinking about variations in the barcode 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 barcode.

The state of this class includes the barcode itself and a flag stating 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 the validation details are delegated to the abstract method.

Implementation for the EAN13

Then the implementation for the 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];
    }

}

Note how the validation algorithm became simple.

The first if uses a regular expression to verify that the barcode has exactly 13 numeric characters. We kill several bunnies with a single beat, because from this point on we can assume several things about the content, as for example the positions of the number vector that we will generate next.

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

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

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

Using

The use 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 use becomes more intuitive:

  • Do not need to pass the object to himself 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 later state
  • It is easier to check the state since we use a Boolean, no need to enter into Strings comparison questions including details such as upper and lower case
  • We can easily extend the routines that use the CodigoBarra to accept new types of barcodes.

4

EAN13 Barcode

The EAN13 barcode belongs to the GS1 System which is an official model of standardization of the processes of product identification and commercial management, which exists since 2006, it consists of a sequence of 13 digits and its symbology represents the following items:

  • Identification of the country of origin of the product.
  • Name of manufacturer company.
  • Identification number of the product.
  • Type.

See an image that illustrates its symbology:

Código de Barras EAN13

Calculation

One of the main requirements to check if a barcode is valid is to calculate the checker digit, see how the calculation is done:

Suppose we are using the barcode : 789162731405 and we want to know the final digit. (Checker)

Add all digits of odd positions (digits 7, 9, 6, 7, 1 and 0): 7 + 9 + 6 + 7 + 1 + 0 = 30

Add all digits of even positions (digits 8, 1, 2, 3, 4 and 5): 8 + 1 + 2 + 3 + 4 + 5 = 23

Multiply the sum of the digits of even positions by 3, see: 23 * 3 = 69

Add the two results of the previous steps, see: 30 + 69 = 99

Determine the number that should be added to the sum result for if you create a multiple of 10, see: 99 + 1 = 100

Therefore, the check digit is 1.

Implementation

Using Object Orientation I created the class CodigoBarraEAN responsible for validating the barcode, the class diagram below shows its structure.

Class diagram CodigoBarraEAN:

Diagrama de classe

Class has only the attribute codigoBarra, it will receive the 13 digits of the barcode. The class has two constructs an empty constructor and the other one that receives the barcode.

Explanation of methods.

All of the methods below are responsible for the validation of the barcode, and only one method can be accessed which is the validar(), it returns a string saying whether the barcode is valid or invalid.

  • Method getCodigoBarra() and setCodigoBarra() public: provides access to the attribute codigoBarra.
  • Method validar() public: is the method responsible for the validation of barcode, it implements the other private methods and compares the checker digit with the checker digit returned by the calculation, if both are equal the code is validated.
  • Method obterNumeroPosicao() private: get position numbers odd or even that will be used in the calculation.
  • Method somarNumeros() private: sum all numbers in a list of type numbers List<Integer>.
  • Method removerDigitoVerificador() private: removes last digit bar code which is the digit checker.
  • Method obterDigitoVerificador() private: get the type checker of the bar code.
  • Method validarEAN() private: checks whether the barcode is within the EAN13 standard containing the 13 digits.
  • Method calcularDigitoVerificador() private: calculates and returns the type barcode checker, to calculate the digit checker it is necessary to pass only the 12 digits without the digit verifier.

Follow all class code CodigoBarraEAN down below:

package codigobarraeanverificador;

import java.util.ArrayList;
import java.util.List;
/** 
 * @author Dener
 */
public class CodigoBarraEAN{
    private String codigoBarra;

    public CodigoBarraEAN(String codigoBarra){
        this.codigoBarra = codigoBarra;
    }

    public CodigoBarraEAN(){
    }

    public String getCodigoBarra(){
        return codigoBarra;
    }

    public void setCodigoBarra(String codigoBarra){
        this.codigoBarra = codigoBarra;
    }

    //Métodos de verificação e validação do codigo de barras.    
    public String validar(CodigoBarraEAN codigoBarra){
        String valido;

        if (validarEAN(codigoBarra.getCodigoBarra())){
            int digitoVerificador = 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 of use of the class CodigoBarraEAN:

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 verify the country of origin and does not validate the product code or company number manufacturer.


Sources:
Unraveling the mysteries of barcodes:
http://www.revistacliche.com.br/2013/10/desvendando-os-misterios-dos-codigos-de-barras/
EAN-13:
https://en.wikipedia.org/wiki/International_Article_Number

1

I couldn’t find a java barcode validator, so I made a GS1 barcode validator, which validates the GTIN-8, GTIN-12, GTIN-13, GTIN-14, GSIN and SSCC barcodes. It is a simple function where it returns true if the barcode is correct.

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

1

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

Today the Apache Commons library itself Ean13checkdigit provides a method for calculating checker digits and validating barcodes.

However, I have observed several situations where only the compute check digit and validate that the length of the EAN is equal to 13 (or 8) does not perform a valid validation. I often found invalid barcodes that completed the required number of digits with zeros to the left and which, by coincidence, generated valid check digits.

By defining the barcode 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 certain system or country.

Thinking about these issues, I developed the following code. The Eanvalidationutil class has the isValid method that can receive Long or String values. First the input parameter is treated to avoid possible errors. Then left zeros are taken from this value and the barcode length is validated. Finally, the calculation of the verifier digit is carried out.

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

Browser other questions tagged

You are not signed in. Login or sign up in order to post.