如何在 JavaScript 中验证 EAN / GTIN 条形码

问题描述 投票:0回答:9

如何在 JavaScript 中检查字符串是否是有效的 EAN / GTIN 条形码?

我需要检查 EAN8、EAN12、EAN13、EAN14、EAN18 以及 GTIN12、GTIN13、GTIN14。

javascript node.js validation barcode
9个回答
33
投票

编辑我还创建了一个npm模块,可以在github上找到。

我创建了一个小型库,它支持 EAN8、EAN12、EAN13、EAN14、EAN18、GTIN12、GTIN13 和 GTIN14。

它可以在 Node.js 和所有现代浏览器中运行。

barcoder.js:

/*!
 * Barcoder
 * Copyright (c) 2013 mifitto GmbH <[email protected]>
 * MIT Licensed
 */

(function() {

  'use strict';

  /**
   * Library version.
   */

  var version = '1.1.0';

  /**
   * Supported formats
   */

  var minValidLength = 6;
  var maxValidLength = 18;
  var usualValidChars = /^\d+$/;

  var formats = {
    'ean8'   : { validChars : /^\d+$/, validLength : 8 },
    'ean12'  : { validChars : /^\d+$/, validLength : 12 },
    'ean13'  : { validChars : /^\d+$/, validLength : 13 },
    'ean14'  : { validChars : /^\d+$/, validLength : 14 },
    'ean18'  : { validChars : /^\d+$/, validLength : 18 },
    'gtin12' : { validChars : /^\d+$/, validLength : 12 },
    'gtin13' : { validChars : /^\d+$/, validLength : 13 },
    'gtin14' : { validChars : /^\d+$/, validLength : 14 }
  };

  /**
   * Validates the checksum (Modulo 10)
   * GTIN implementation factor 3
   *
   * @param  {String} value The barcode to validate
   * @return {Boolean}
   * @api private
   */

  var validateGtin = function( value ) {

    var barcode = value.substring( 0, value.length - 1 );
    var checksum = parseInt( value.substring( value.length - 1 ), 10 );
    var calcSum = 0;
    var calcChecksum = 0;

    barcode.split('').map(function( number, index ) {
      number = parseInt( number, 10 );
      if ( value.length % 2 === 0 ) {
        index += 1;
      }
      if ( index % 2 === 0 ) {
        calcSum += number;
      }
      else {
        calcSum += number * 3;
      }
    });

    calcSum %= 10;
    calcChecksum = (calcSum === 0) ? 0 : (10 - calcSum);

    if ( calcChecksum !== checksum ) {
      return false;
    }

    return true;

  };

  /**
   * Barcoder class
   *
   * @param {string}  format    See formats
   * @param {Object}  options   Valid option `enableZeroPadding`, defaults to `true`
   * @api public
   */

  var Barcoder = function ( format, options ) {

    if ( format && !formats[format] ) throw new Error( '"format" invalid' );

    this.format = (format) ? formats[format] : 'autoSelect';
    this.options = (options) ? options : { enableZeroPadding : true };

    if ( !this.options.enableZeroPadding ) {
      this.options.enableZeroPadding = true;
    }

  };

  /**
   * Validates a barcode
   *
   * @param  {string}  barcode   EAN/GTIN barcode
   * @return {Boolean}
   * @api public
   */

  Barcoder.prototype.validate = function( barcode ) {

    var self = this;

    if ( self.format === 'autoSelect' ) {

      if ( barcode.length < minValidLength || barcode.length > maxValidLength ) {
        return false;
      }

      var isValidGtin = validateGtin( barcode );
      var paddedBarcode = barcode;
      var successfullyPadded = false;

      if ( !isValidGtin ) {
        var possiblyMissingZeros = maxValidLength - barcode.length;
        while( possiblyMissingZeros-- ) {
          paddedBarcode = '0' + paddedBarcode;
          if ( validateGtin( paddedBarcode ) ) {
            isValidGtin = true;
            successfullyPadded = true;
            break;
          }
        }
      }

      return {
        possibleType: (barcode.length > 8) ? 'GTIN' + barcode.length : 'EAN8 / padded GTIN',
        isValid: isValidGtin
      };

    }

    var validChars = self.format.validChars;
    var validLength = self.format.validLength;
    var enableZeroPadding = self.options.enableZeroPadding;

    if ( validChars.exec( barcode ) === null ) {
      return false;
    }

    if ( enableZeroPadding && barcode.length < validLength ) {
      var missingZeros = validLength - barcode.length;
      while( missingZeros-- ) {
        barcode = '0' + barcode;
      }
    }
    else if ( !enableZeroPadding && barcode.length != validLength ) {
      return false;
    }
    else if ( barcode.length > validLength ) {
      return false;
    }

    return validateGtin( barcode );

  };

  /**
   * Export
   */

  if ( 'undefined' !== typeof module && module.exports ) {
    module.exports = Barcoder;
    exports.version = version;
  }

  if ( 'undefined' === typeof ender ) {
    this['Barcoder'] = Barcoder;
  }

  if ( 'function' === typeof define && define.amd ) {
    define('Barcoder', [], function () {
      return Barcoder;
    });
  }

}).call( this );

安装:

$ npm install barcoder

用途:

var Barcoder = require('barcoder');

var ean1 = '0016T20054453';
var ean2 = '9330071314999';

var validator = new Barcoder('ean13');

console.log( '%s ean1 is valid: %s', ean1, validator.validate( ean1 ) );
console.log( '%s ean2 is valid: %s', ean1, validator.validate( ean2 ) );

// or /w automatic type selection

validator = new Barcoder();

var validation1 = validator.validate( ean1 );
var validation2 = validator.validate( ean2 );

console.log( '%s is valid: %s and has guessed type: %s', ean1, validation1.isValid, validation1.possibleType );
console.log( '%s is valid: %s and has guessed type: %s', ean2, validation2.isValid, validation2.possibleType );

18
投票

我不知道为什么,但 @doms 解决方案对我来说无法正常工作。我还想计算新代码以及验证旧代码。我最终得到了这个,我已经验证至少可以在我的浏览器中工作:

function eanCheckDigit(s){
    var result = 0;
    for (let counter = s.length-1; counter >=0; counter--){
        result = result + parseInt(s.charAt(counter)) * (1+(2*(counter % 2)));
    }
    return (10 - (result % 10)) % 10;
}

2020 更新 - 必须在计数器前面添加

let
,否则会说计数器未定义。

2020 年第二次更新 - 经过多次斗争,我意识到这个公式仅适用于传入的 UPC 为 10(甚至长度为数字)。如果您传递 11 位数字,则此方法不起作用。因此,我对其进行了修改,使其适用于任何长度的 UPC。让我知道这是否可以写得更干净。

function eanCheckDigit(s){
        let result = 0;
        let i = 1;
        for (let counter = s.length-1; counter >=0; counter--){
            result = result + parseInt(s.charAt(counter)) * (1+(2*(i % 2)));
            i++;
        }
        return (10 - (result % 10)) % 10;
    }

8
投票

这是一个简短的版本,可以检查EAN13校验位是否有效:

  var checkSum = ean.split('').reduce(function(p,v,i) {
    return i % 2 == 0 ? p + 1 * v : p + 3 * v;
    }, 0);
  if (checkSum % 10 != 0) {
    alert('error');
  }

5
投票

GS1 US 发布了 GTIN 的校验位计算算法。它使用填充来计算各种条形码,实际上比我在上面找到的其他方法简单得多。

它适用于 GTIN 条形码:GTIN-8、GTIN-12 (UPC)、GTIN-13 (EAN) 和 GTIN-14 (ITF-14)。

function isValidBarcode(value) {
  // We only allow correct length barcodes
  if (!value.match(/^(\d{8}|\d{12,14})$/)) {
    return false;
  }

  const paddedValue = value.padStart(14, '0');

  let result = 0;
  for (let i = 0; i < paddedValue.length - 1; i += 1) {
    result += parseInt(paddedValue.charAt(i), 10) * ((i % 2 === 0) ? 3 : 1);
  }

  return ((10 - (result % 10)) % 10) === parseInt(paddedValue.charAt(13), 10);
}

2
投票

这就是我想到的:

/**
 * Test a string for valid EAN5 EAN8 EAN13 EAN14 EAN18
 * @see: https://www.activebarcode.com/codes/ean13.html
 * @param  {string} ean A string to be tested
 * @return {boolean} true for a valid EAN
 * @author Vitim.us <https://stackoverflow.com/a/65928239/938822>
 */
function isValidEAN(ean) {
    function testChecksum(ean) {
        const digits = ean.slice(0, -1);
        const checkDigit = ean.slice(-1) | 0;
        let sum = 0;
        for (let i = digits.length - 1; i >= 0; i--) {
            sum += (digits.charAt(i) * (1 + (2 * (i % 2)))) | 0;
        }
        sum = (10 - (sum % 10)) % 10;
        return sum === checkDigit;
    }
    ean = String(ean);
    const isValidLength = ean.length === 18 || ean.length === 14 || ean.length === 13 || ean.length === 8 || ean.length === 5;
    return isValidLength && /^\d+$/.test(ean) && testChecksum(ean);
}


1
投票

这是我的解决方案,使用规范检查不同长度的条形码以计算末尾的校验位(见注释):

// ean/gtin validation for 8, 12, 13 & 14 digit barcodes
function codeOnBlur(barcode) {

    var barcodeLengthArr = [8, 12, 13, 14];
    var allowedChars = new RegExp(/\d{8,14}/); // >7 & <15
    // put numbers in array and convert to type Int.
    var barcodeArray = barcode.split('');
    for( var i = 0; i < barcodeArray.length; i++) {
        barcodeArray[i] = parseInt(barcodeArray[i], 10);
    }
    // get the last digit for checking later
    var checkDigit = barcodeArray.slice(-1)[0];
    // we'll need a to compare it to this:
    var remainder = 0;

    // check if input (barcode) is in the array and check against the regex. 
    if (($.inArray(barcode.length, barcodeLengthArr) > -1) && (allowedChars.test(barcode))) {
        console.log("barcodeArray ", barcodeArray, " :: checkDigit ", checkDigit);

        // Pop the last item from the barcode array, test if the length is 
        // odd or even (see note on calculating the check digit) and 
        // multiply each item in array based in position: 
        var total = 0;
        barcodeArray.pop();
        // odd length after pop
        if (barcodeArray.length % 2 === 1) {
            for (var i = barcodeArray.length - 1; i >= 0; i--) {
                barcodeArray[i] = i % 2 === 0 ? barcodeArray[i] * 3 : barcodeArray[i] * 1;
                total += barcodeArray[i];
            }
        // even length after pop
        } else if (barcodeArray.length % 2 === 0) {

            for (var i = barcodeArray.length - 1; i >= 0; i--) {
                barcodeArray[i] = i % 2 === 0 ? barcodeArray[i] * 1 : barcodeArray[i] * 3;
                total += barcodeArray[i];
            }
        } else {
            // validation passed = false
        }
        // calculate the remainder of totalrounded up to nearest multiple of 10:
        remainder = (Math.ceil((total + 1) / 10) * 10) - total;
        console.log("loop total = ", total, ", remainder: ", remainder);

        if ( remainder === checkDigit ) {
            //validation passed = true;
            return;
        } else {
              //validation passed = false; 
        }

    } else {
        //validation Passed = false;
    }
}

我确信这段代码可以整理一些:)

手动检查“完整性位”或校验位:

     barcode: 13:   4  0  1  1  2  0  0  2  9  6  9  0  8
               8:                  5  0  8  1  8  9  0  7

  multiplier:       3  1  3  1  3  1  3  1  3  1  3  1  check digit  

要逆向使用 8 位代码:

0*1 + 9*3 + 8*1 + 1*3 + 8*1 + 0*3 + 5*1 = 73

Difference from 73 to 80 is 7 (the specification will have you round up to
 the nearest power of 10).   

7既是校验位,又是80-73的余数。


0
投票

我成功创建了一个简化的条形码验证功能,测试了EAN 数字检查(Modulo-10 算法),该功能可用于不同的数字条形码和图书 ISBN 编号。

它适用于任何长度的 EAN 条形码,例如:GTIN-8、GTIN-12 (UPC)、GTIN-13 (EAN)、ISBN、GTIN-14 (ITF-14) 和 GTIN18 (SSCC)

function isValidBarcode(number) {
  const checkDigit = String(number).slice(0, -1).split('').reverse().reduce((sum, v, i) => sum + v * (i % 2 || 3), 0)*9%10
  return /^\d+$/.test(number) && String(checkDigit) === String(number).at(-1)
}

如果您的目标是特定长度的条形码,您还可以检查:

const isValidBarcodeAndSize =  [12,13].includes(String(myBarcode).length) && isValidBarcode(myBarcode)

0
投票

如果您只需要 UPC 和 EAN,您可以考虑使用这个简单的功能。

function validateUpcEan(code) {
    let c = code.split(""), d = Number(c.pop()), o = e = 0;
    for (i = 0; i < c.length; i++) {
        if (i % 2 === 0)
            o += Number(c[i]);
        else
            e += Number(c[i]);
    }
    return ((o * (code.length == 12 ? 3 : 1)) + (e * (code.length == 13 ? 3 : 1)) + d) % 10 === 0;
}

-1
投票

如果此代码有点太长,我很抱歉,但这是我用于验证 EAN13 条形码的代码:

function isBarcode(barcode) {
    if (typeof barcode === 'number') {
        throw 'RuntimeError: Barcode MUST NOT be in number format'
    } else if (barcode.length!==12) {
        throw 'RuntimeError: String length is not 12'
    };
    var _= barcode.toString().split("")
    var _1 = 0
    var _2 = 0
    var __
    for ($=0;$<=10;++$) {
        _1+=+_[$]
    };for ($=10;$>=0;$-=2) {
       _2+=+_[$]
    };_2*=2
    var _3 = _1+_2
    __=+_3.toString().substring(1,2)
    if (__>9) {
        __=+_3.toString().substring(1,2)
    } else if (__===0) {
        __=10
    };
    __=10-__
    if (__===+_[11]) {
        return true
    }
    return false
};
© www.soinside.com 2019 - 2024. All rights reserved.