【问题标题】:How to validate a EAN / GTIN barcode in JavaScript如何在 JavaScript 中验证 EAN / GTIN 条形码
【发布时间】:2012-11-16 07:40:42
【问题描述】:

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

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

【问题讨论】:

    标签: javascript node.js validation barcode


    【解决方案1】:

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

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

    它适用于 node.js 和所有现代浏览器。

    barcoder.js:

    /*!
     * Barcoder
     * Copyright (c) 2013 mifitto GmbH <dominik@mifitto.com>
     * 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 );
    

    【讨论】:

      【解决方案2】:

      我不知道为什么,但@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 年第 2 次更新 - 经过大量的斗争,我意识到这个公式只适用于 10 位(甚至长度为数字)的 UPC。如果你传递一个 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;
          }
      

      【讨论】:

      • 您可以用更简单的 (1+(2*(counter % 2)) 替换 Math.pow 表达式,结果相同。
      • 似乎是第四行的括号错误。一个右括号太多了。
      • 修复了!很好发现。
      • 我不确定为什么这段代码对你有用。对我来说,直到我在counter 前面加上let 之前,for 循环都有错误。它说counter 没有定义。
      • 我猜这是因为答案相当陈旧(2013 年),并且 js 从那时起就取得了进展。随意编辑答案以修复错误!
      【解决方案3】:

      这是一个简短的版本,可以检查 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');
        }
      

      【讨论】:

      • 酷,这正是我需要的。只需添加 ean = ""+ean;将其转换为字符串以使用 .split 函数
      【解决方案4】:

      如果此代码有点太长,我很抱歉,但这是我用于验证 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
      };
      

      【讨论】:

      • 请解释为什么这样做。这将防止 OP 简单地复制和粘贴您的代码而不了解它是如何工作的,或者它是否完全适合他们手头的任务。
      • @rayryeng:我不会在不请求许可的情况下复制任何东西;)我喜欢聪明的解决方案,但我认为这段代码并不是真正可维护和/或可读的。我也欢迎解释。
      • @dom - 哈哈哈我同意 :)
      【解决方案5】:

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

      // 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 的余数。

      【讨论】:

        【解决方案6】:

        这是我想出的:

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

        【讨论】:

          【解决方案7】:

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

          【讨论】:

            猜你喜欢
            • 2012-07-02
            • 2012-04-25
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 2010-12-06
            • 1970-01-01
            • 2021-06-05
            相关资源
            最近更新 更多