【问题标题】:How do I validate a UPC or EAN code?如何验证 UPC 或 EAN 代码?
【发布时间】:2012-04-25 23:28:18
【问题描述】:

我需要一个 C# .NET 函数来评估输入或扫描的条形码是否是有效的Global Trade Item Number(UPC 或 EAN)。

条形码编号的最后一位数字是计算机校验位,可确保条形码正确组成。 GTIN Check Digit Calculator

【问题讨论】:

  • @Zack 我假设您现在已经有了答案,但我想指出,如果您的系统打算处理 ISBN-10 代码(随着旧书的脱落,这些代码最终会消失市场),您需要对此进行检查。您的问题特定于 GTIN,但 ISBN-10 可以转换为 ISBN-13,相当于 EAN / GTIN-13。原因:ISBN-10 是模 11,因此使用字母“X”作为可能的校验位来表示数字 10。在这里仅查找数字会失败,除非您先转换为 ISBN-13。
  • 仅供参考,算法描述here

标签: c# .net barcode checksum


【解决方案1】:

如果有人需要 AlexDev 的答案的 javascript 版本,请看这里。我注意到 AlexDev 的答案也适用于 ITF 条形码,而其他一些答案则不适用。

function isValidGtin(barcode) {
    var code = "00000" + barcode;
    code = code.substring(code.length - 14);
    var sum = 0;
    for (var i = 0; i < code.length; i++) {
        sum += code[i] * ((i % 2 == 0) ? 3 : 1);
    }
    return (sum % 10) == 0;
}

【讨论】:

    【解决方案2】:

    我今天花了一些时间来完全支持 GTIN-8、GTIN-12、GTIN-13 和 GTIN-14 的校验和计算。大多数算法示例只支持 GTIN-13 或者很丑。

    这个怎么样?

    public static char CalculateCheckSumDigit(string code)
    {
       // https://www.gs1.org/services/how-calculate-check-digit-manually
       var reversed = code.Reverse().Skip(1);
    
       int sum = 0;
       int multiplier = 3;
       foreach(char c in reversed)
       {
           if (multiplier == 3)
           {
               sum += (c - 0x30) * multiplier;
               multiplier = 1;
           }
           else
           {
               sum += (c - 0x30) * multiplier;
               multiplier = 3;
           }
       }
    
       int closestTenth = (sum + 9) / 10 * 10;
    
       return (char)((closestTenth - sum) + 0x30);
    }
    

    【讨论】:

      【解决方案3】:

      我还需要验证大量的 EAN-13,并在搜索时发现了这个问题。我不喜欢最受好评的答案的样子。处理大型数据集时可能的字符串分配过多。与仅验证每个字符是否为数字相比,正则表达式也很慢。

      在我的 8851 个 EAN-13 数字数据集上运行最受好评的问题中使用的验证逻辑在我的旧笔记本电脑上花费了 47 毫秒,而我自己的实现只花费了 2 毫秒。 这里的字符串分配也少得多。

      private static bool IsValidGtin(ReadOnlySpan<char> input, byte length)
      {
          if (input.Length != length)
          {
              return false;
          }
      
          if (!char.IsDigit(input[^1]))
          {
              return false;
          }
      
          var sum = 0d;
          var multiplyByThree = true;
          var inputWithoutCheckDigit = input[..^1];
          for (var i = inputWithoutCheckDigit.Length - 1; i >= 0; i--)
          {
              var currentChar = inputWithoutCheckDigit[i];
              if (!char.IsDigit(currentChar))
              {
                  return false;
              }
      
              var value = char.GetNumericValue(currentChar);
              if (multiplyByThree)
              {
                  sum += value * 3;
              }
              else
              {
                  sum += value;
              }
      
              multiplyByThree = !multiplyByThree;
          }
          
          var checkDigit = char.GetNumericValue(input[^1]);
      
          return (sum + checkDigit) % 10 == 0;
      
      }
      

      如果需要,我会写更多关于它的细节here

      【讨论】:

        【解决方案4】:

        EAN8、EAN12、EAN13、EAN14 在快船/港口

        function DigitEan(cCodigo)
            local cCode := AllTrim(cCodigo)
            local nLen := Len(cCode)
            local cc := 0
            local i := 0
        
            for i:=1 to nLen-1
                cc+=(asc(substr(cCode,nLen-i,1))-48)*iif(i%2==0,1,3)
            next
        
        return ( (10-(cc%10))%10 )
        

        【讨论】:

        • 请仅使用 OP 要求的语言回答。
        • 这个问题已经有多种语言的答案。如果它说明了不同的方法或算法,即使语法不同,它仍然可以增加价值。这个答案的唯一问题是没有解释这里发生了什么或为什么。仅靠代码是不够的。
        【解决方案5】:

        我编写了这个 Python 代码来快速简洁地验证 UPC 代码:

        def upc_validate(upc):
            # check if upc is exactly 12 digits
            upc = str(upc)
            if len(upc) != 12 or len(list(filter(lambda x: 48 <= ord(x) <= 57, upc))) != 12:
                return False
        
            # validate CRC / check digit (sum of digits is a product of 10 after multiplying each odd digit by CRC)
            digits = list(map(int, upc))
            crc = digits[-1]
            total = sum([digit if i & 1 else digit * crc for i, digit in enumerate(digits)])
            return total % 10 == 0
        

        【讨论】:

          【解决方案6】:

          根据卢西亚诺的回答。

          我删除了 Linq 依赖项,因为我在 Java 中也需要它。将填充删除为 14 位,以便用于计算较长 ITF 条码的校验位。

          长度不均匀的代码由(i + code.Length % 2) 处理,这使得31313 模式13131 用于长度不均匀的代码。它避免了创建对象,并且需要 134 毫秒(在我的 PC 上)来计算所有 EAN-8 代码的校验位。

          我知道这个问题是关于 IsValidEan() 方法的,但如果你有一个包含方法的库,你可能无论如何都需要这个。 IsValidEan() 应该在长度和其他检查之后调用它进行校验位验证。

          /// <summary>
          /// Calculate check digit for UPC-A, EAN-13, ITF-14 (or any ITF)
          /// </summary>
          /// <param name="code">This should include the check digit (append a fake check digit if needed)</param>
          /// <returns></returns>
          public static char CalculateCheckDigit(string code)
          {
              // Don't include check digit in the sum (< code.Length - 1)
              int sum = 0;
              for (int i = 0; i < code.Length - 1; i++)
                  sum += (code[i] - '0') * (((i + code.Length % 2) % 2 == 0) ? 3 : 1);
          
              return (char)((10 - (sum % 10)) % 10 + '0');
          }
          

          Java 版本:

          public static char CalculateCheckDigit(String code) {
              int sum = 0;
              for(int i = 0; i < code.length() - 1; i++)
                  sum += ((int)code.charAt(i) - (int)'0') * (((i + code.length() % 2) % 2 == 0) ? 3 : 1);
          
              return (char)((10 - (sum % 10)) % 10 + (int)'0');
          }
          

          【讨论】:

            【解决方案7】:

            我找到了这个 Nuget 包:https://www.nuget.org/packages/ProductCodeValidator/

            github中的代码是:https://github.com/ThomasPe/ProductCodeValidator

            使用方法如下:

            using ProductCodeValidator;
            
            bool IsValidEAN(string EAN)
            {
                return EanValidator.IsValidEan(testEan));
            }
            

            【讨论】:

              【解决方案8】:

              def check_digit():

                      users_gtin=raw_input("enter first seven digits of gtin ")
              
                      gtin_seven_digits=unicode(users_gtin)
                      
                      
                      if len(gtin_seven_digits) == 7 and gtin_seven_digits.isnumeric():
                          ck = ((((int(gtin_seven_digits[0])) + (int(gtin_seven_digits[2])) + (int(gtin_seven_digits[4])) + (int(gtin_seven_digits[6])))*3) + ((int(gtin_seven_digits[1])) + (int(gtin_seven_digits[3])) + (int(gtin_seven_digits[5])))) %10
                          final_ck = 10-ck
              
                          if final_ck == 10:
                              final_ck=0
                              print "Is your check digit",final_ck,"?"
                      
                          else:
                              print "Is your check digit",final_ck,"?"
                              
                      else:
                          print "please try typing an seven digit number"
                          check_digit()
                                                                                                                                                  
              
                      choice=raw_input("enter (a) to restart or press anything other than the letter  a  to end this program <<<< ").upper()
                      
              
                      if choice == "A":
                          check_digit()
                      
              

              check_digit()

              可能不是最有效的,但希望对您有所帮助..

              【讨论】:

              • 稍微解释一下答案可能会有所帮助,而不仅仅是编写代码让他们学习
              • 抱歉,我是这个网站的新手,所以不知道如何添加 cmets,我发现使用 # 会使代码看起来更复杂,更难理解,但不知道如何添加添加描述@winhowes
              【解决方案9】:

              我知道问题是在 .net/C# 的上下文中。尽管如此,我还是在这个页面上寻求相同问题的答案,但在 Groovy 上下文中。

              由于我实际上成功地使用此页面上的信息来解决我自己的问题,所以我想我会分享结果。
              尤其是 AlexDev、Zack Peterson 和 Mitch 的回答对我很有帮助。

              /*
              Check digit calculation is based on modulus 10 with digits in an odd
              position (from right to left) being weighted 1 and even position digits
              being weighted 3. 
              For further information on EAN-13 see:
              Wikipedia - European Article Number: http://en.wikipedia.org/wiki/International_Article_Number_%28EAN%29
              Implementation based on http://stackoverflow.com/questions/10143547/how-do-i-validate-a-upc-or-ean-code
              Tests can be found there too
              */
              boolean isValidEan(String code){
                returnValue = false
                if (code ==~ /\d{8}|\d{12,14}/){ //Matches if String code contains 8, 12, 13 or 14 digits
                  assert [8,12,13,14].contains(code.size())
                  code = code.padLeft(14, '0')
                  assert code.size() == 14
                  int sum = 0
                  code.eachWithIndex{ c, i ->
                    sum += c.toInteger() * ((i % 2 == 0) ? 3 : 1)
                  }
                  returnValue = sum % 10 == 0
                }
                return returnValue
              }
              

              【讨论】:

                【解决方案10】:

                GS1 US 发布 GTIN 校验位计算算法 PDF 文档(删除了不断变化的链接)。

                以下代码使用 linq 检查 GTIN 条码的最后一位数字:GTIN-8、GTIN-12 (UPC)、GTIN-13 (EAN) 和 GTIN-14 (ITF-14)。

                private static Regex _gtinRegex = new System.Text.RegularExpressions.Regex("^(\\d{8}|\\d{12,14})$");
                public static bool IsValidGtin(string code)
                {
                    if (!(_gtinRegex.IsMatch(code))) return false; // check if all digits and with 8, 12, 13 or 14 digits
                    code = code.PadLeft(14, '0'); // stuff zeros at start to garantee 14 digits
                    int[] mult = Enumerable.Range(0, 13).Select(i => ((int)(code[i] - '0')) * ((i % 2 == 0) ? 3 : 1)).ToArray(); // STEP 1: without check digit, "Multiply value of each position" by 3 or 1
                    int sum = mult.Sum(); // STEP 2: "Add results together to create sum"
                    return (10 - (sum % 10)) % 10 == int.Parse(code[13].ToString()); // STEP 3 Equivalent to "Subtract the sum from the nearest equal or higher multiple of ten = CHECK DIGIT"
                }
                

                【讨论】:

                【解决方案11】:

                我遇到了类似的问题,谷歌把我带到了这个页面。 我需要为标签生成程序计算大量条形码的校验位。我首先从上面 Luciano Carvalho 的答案的变体开始,但我对将字符串转换为 char 到 int 有点好奇。我怀疑我可以稍微提高性能。

                请注意,验证是在此函数之外进行的。由于我要生成大量条形码,因此此功能更适合速度。

                int CalculateCheckDigit(ulong label)
                {
                    int sum = 0;
                    bool isEven=true;
                    while(label>0)
                    {
                        if(isEven)
                            sum += (int)(label % 10) * 3;
                        else
                            sum += (int)(label % 10) * 1;
                        isEven = !isEven;
                        label /= 10;
                    }
                
                    return (10 - (sum % 10)) % 10;
                }
                

                【讨论】:

                【解决方案12】:

                上述解决方案计算校验位并将其与给定数字进行比较,忽略了它旨在以更简单的方式进行验证的事实。

                1. 将所有数字(包括校验位)乘以 3 或 1 并求和。
                2. 检查总和是否是 10 的倍数

                根据卢西亚诺的回答:

                private static Regex _gtinRegex = new Regex("^(\\d{8}|\\d{12,14})$");
                public static bool IsValidGtin(string code)
                {
                    if (!(_gtinRegex.IsMatch(code))) return false;
                    code = code.PadLeft(14, '0');
                    int sum = code.Select((c,i) => (c - '0')  * ((i % 2 == 0) ? 3 : 1)).Sum();
                    return (sum % 10) == 0;
                }
                

                【讨论】:

                  【解决方案13】:
                      private bool ValidateCheckDigit()
                      {
                  
                          Int32 _num = 0;
                          Int32 _checkdigit = 0;
                  
                          for (int i = 0; i < CurrentUpcInfo.UpcCode.Length; i++)
                          {
                              if (i % 2 == 0)
                              {
                                  _num += (3 * Convert.ToInt32(CurrentUpcInfo.UpcCode.Substring(i, 1)));
                              }
                              else
                              {
                                  _num += Convert.ToInt32(CurrentUpcInfo.UpcCode.Substring(i, 1));
                              }
                  
                          }
                          _num = Math.Abs(_num) + 10;  // in case num is a zero
                          _checkdigit = (10 - (_num % 10)) % 10;
                  
                  
                          if (Convert.ToInt32(CurrentUpcInfo.Checkdigit) == _checkdigit)
                              return true;
                  
                          return false;
                  
                      }
                  

                  【讨论】:

                    【解决方案14】:
                    /// <summary>
                    /// Validates a GTIN (UPC/EAN) using the terminating check digit
                    /// </summary>
                    /// <param name="code">the string representing the GTIN</param>
                    /// <returns>True if the check digit matches, false if the code is not 
                    /// parsable as a GTIN or the check digit does not match</returns>
                    public static bool IsValidGtin(string code)
                    {
                        if (string.IsNullOrWhiteSpace(code))
                            return false;
                        if (code.Length != 8 && code.Length != 12 && code.Length != 13 
                            && code.Length != 14)
                            // wrong number of digits
                            return false;
                    
                        int sum = 0;
                        for (int i = 0; i < code.Length - 1 /* do not include check char */; i++)
                        {
                            if (!char.IsNumber(code[i]))
                                return false;
                    
                            var cchari = (int)char.GetNumericValue(code[i]);
                            // even (from the right) characters get multiplied by 3
                            // add the length to align right
                            if ((code.Length + i) % 2 == 0)
                                sum += cchari * 3;
                            else
                                sum += cchari;
                        }
                    
                        // validate check char
                        char checkChar = code[code.Length - 1];
                        if (!char.IsNumber(checkChar))
                            return false;
                    
                        int checkChari = (int)char.GetNumericValue(checkChar);
                        return checkChari == (10 - (sum % 10)) % 10;
                    }
                    

                    测试用例:

                        [TestMethod()]
                        public void IsValidGtinTest_Valid()
                        {
                            string[] valid = new[] {
                                "085126880552",
                                "0085126880552",
                                "00085126880552",
                                "0786936226355",
                                "0719852136552"
                            };
                            foreach (var upc in valid)
                                Assert.IsTrue(IdentifierUtilities.IsValidGtin(upc), upc);
                        }
                    
                        [TestMethod()]
                        public void IsValidGtinTest_Invalid()
                        {
                            string[] invalid = new[] {
                                "0058126880552",
                                "58126880552",
                                "0786936223655",
                                "0719853136552",
                                "",
                                "00",
                                null,
                                "123456789123456789123456789",
                                "1111111111111"
                            };
                            foreach (var upc in invalid)
                                Assert.IsFalse(IdentifierUtilities.IsValidGtin(upc), upc);
                        }
                    

                    【讨论】:

                      【解决方案15】:

                      可变长度 EAN

                          public static bool IsValidEan13(string eanBarcode)
                          {
                              return IsValidEan(eanBarcode, 13);
                          }
                      
                          public static bool IsValidEan12(string eanBarcode)
                          {
                              return IsValidEan(eanBarcode, 12);
                          }
                      
                          public static bool IsValidEan14(string eanBarcode)
                          {
                              return IsValidEan(eanBarcode, 14);
                          }
                      
                          public static bool IsValidEan8(string eanBarcode)
                          {
                              return IsValidEan(eanBarcode, 8);
                          }
                      
                          private static bool IsValidEan(string eanBarcode, int length)
                          {
                              if (eanBarcode.Length != length) return false;
                              var allDigits = eanBarcode.Select(c => int.Parse(c.ToString(CultureInfo.InvariantCulture))).ToArray();
                              var s = length%2 == 0 ? 3 : 1;
                              var s2 = s == 3 ? 1 : 3;
                              return allDigits.Last() == (10 - (allDigits.Take(length-1).Select((c, ci) => c*(ci%2 == 0 ? s : s2)).Sum()%10))%10;
                          }
                      
                          [Test]
                          [TestCaseSource("Ean_13_TestCases")]
                          public void Check_Ean13_Is_Valid(string ean, bool isValid)
                          {
                              BlinkBuilder.IsValidEan13(ean).Should().Be(isValid);
                          }
                      
                          private static IEnumerable<object[]> Ean_13_TestCases()
                          {
                              yield return new object[] { "9781118143308", true };
                              yield return new object[] { "978111814330", false };
                              yield return new object[] { "97811181433081", false };
                              yield return new object[] { "5017188883399", true };
                          }
                      
                          [Test]
                          [TestCaseSource("Ean_8_TestCases")]
                          public void Check_Ean8_Is_Valid(string ean, bool isValid)
                          {
                              BlinkBuilder.IsValidEan8(ean).Should().Be(isValid);
                          }
                      
                          private static IEnumerable<object[]> Ean_8_TestCases()
                          {
                              yield return new object[] { "12345670", true };
                              yield return new object[] { "12345679", false };
                              yield return new object[] { "55432214", true  };
                              yield return new object[] { "55432213", false };
                              yield return new object[] { "55432215", false };
                          }
                      

                      编辑

                      我正在为其构建此代码的项目现已启动并运行 - 它是综合条形码数据库和工具集的一部分 - 并包括一个批量条形码验证器(非注册用户批量为 100 个,注册用户为 10,000 个)-@ 987654321@

                      【讨论】:

                        【解决方案16】:
                        private void button1_Click(object sender, EventArgs e)
                        {
                            string code = textBox1.Text;
                            string sBarcode = string.Empty;
                            sBarcode = IsValidGtin(code);
                            lblBarCode.Text = sBarcode;
                        }
                        public static string IsValidGtin(string code)
                        {
                        
                            //if (code != (new Regex("[^0-9]")).Replace(code, ""))
                            //{
                            //    // is not numeric
                            //    return false;
                            //}
                            // pad with zeros to lengthen to 14 digits
                            switch (code.Length)
                            {
                                case 2:
                                    code = code + "000000000";
                                    break;
                                case 3:
                                    code = code + "00000000";
                                    break;
                                case 4:
                                    code = code + "0000000";
                                    break;
                                case 5:
                                    code = code + "000000";
                                    break;
                                case 6:
                                    code = code + "00000";
                                    break;
                                case 7:
                                    code = code + "0000";
                                    break;
                                case 8:
                                    code = code + "000";
                                    break;
                                case 9:
                                    code = code + "00";
                                    break;
                                case 10:
                                    code = code + "0";
                                    break;
                                case 11:
                                    break;
                                case 12:
                                    code = code.Substring(0, 11);
                                    break;
                                //default:
                                // wrong number of digits
                                //  return false;
                            }
                            // calculate check digit
                            int[] a = new int[12];
                            a[0] = int.Parse(code[0].ToString()) * 3;
                            a[1] = int.Parse(code[1].ToString());
                            a[2] = int.Parse(code[2].ToString()) * 3;
                            a[3] = int.Parse(code[3].ToString());
                            a[4] = int.Parse(code[4].ToString()) * 3;
                            a[5] = int.Parse(code[5].ToString());
                            a[6] = int.Parse(code[6].ToString()) * 3;
                            a[7] = int.Parse(code[7].ToString());
                            a[8] = int.Parse(code[8].ToString()) * 3;
                            a[9] = int.Parse(code[9].ToString());
                            a[10] = int.Parse(code[10].ToString()) * 3;
                            //a[11] = int.Parse(code[11].ToString());
                            //a[12] = int.Parse(code[12].ToString()) * 3;
                            int sum = a[0] + a[1] + a[2] + a[3] + a[4] + a[5] + a[6] + a[7] + a[8] + a[9] + a[10];
                            string check = Convert.ToString((10 - (sum % 10)));
                            // evaluate check digit
                            // int last = int.Parse(code[13].ToString());
                            // return check == last;
                            code = code + check;
                            return code;
                        }
                        

                        【讨论】:

                        • 这段代码是解决问题的一个非常糟糕的方法。它只能在程序员出于某种原因无法访问循环结构的情况下使用。注释“用零填充以延长到 14 位”与填充到 11 的代码不一致,并且无论如何,大部分 EAN 代码预计有 13 位。我不知道@Adi Lester 编辑了什么,但这段代码没有为问题提供正确的解决方案。
                        • @ThunderGr 我只是修复了答案的格式。您可以通过单击“已编辑...”链接查看修订历史记录
                        【解决方案17】:
                        public static bool IsValidGtin(string code)
                        {
                            if (code != (new Regex("[^0-9]")).Replace(code, ""))
                            {
                                // is not numeric
                                return false;
                            }
                            // pad with zeros to lengthen to 14 digits
                            switch (code.Length)
                            {
                                case 8:
                                    code = "000000" + code;
                                    break;
                                case 12:
                                    code = "00" + code;
                                    break;
                                case 13:
                                    code = "0" + code;
                                    break;
                                case 14:
                                    break;
                                default:
                                    // wrong number of digits
                                    return false;
                            }
                            // calculate check digit
                            int[] a = new int[13];
                            a[0] = int.Parse(code[0].ToString()) * 3;
                            a[1] = int.Parse(code[1].ToString());
                            a[2] = int.Parse(code[2].ToString()) * 3;
                            a[3] = int.Parse(code[3].ToString());
                            a[4] = int.Parse(code[4].ToString()) * 3;
                            a[5] = int.Parse(code[5].ToString());
                            a[6] = int.Parse(code[6].ToString()) * 3;
                            a[7] = int.Parse(code[7].ToString());
                            a[8] = int.Parse(code[8].ToString()) * 3;
                            a[9] = int.Parse(code[9].ToString());
                            a[10] = int.Parse(code[10].ToString()) * 3;
                            a[11] = int.Parse(code[11].ToString());
                            a[12] = int.Parse(code[12].ToString()) * 3;
                            int sum = a[0] + a[1] + a[2] + a[3] + a[4] + a[5] + a[6] + a[7] + a[8] + a[9] + a[10] + a[11] + a[12];
                            int check = (10 - (sum % 10)) % 10;
                            // evaluate check digit
                            int last = int.Parse(code[13].ToString());
                            return check == last;
                        }
                        

                        【讨论】:

                        • 对,UPC 代码中的最后一位是“模校验”位。在 Petzold 的书代码中可以找到一个非常平易近人的解释。
                        • 我希望您不打算在此系统上扫描 ISBN-10 条码(过去在 ISBN-13 之前使用过的书籍)。 ISBN-10 是模 11。我可以使用字母“X”作为校验位来表示数字 10。可能的解决方案:先转换为 ISBN-13(相当于 EAN-13 / GTIN-13)。
                        • 注意:ISBN-10 已于 2007 年 1 月 1 日被 ISBN-13 取代。这并不意味着当前上架的书籍不会同时带有这两种代码(向后兼容)。如果该系统具有人工输入界面并且有任何机会处理书籍,您将需要防止他们选择 ISBN-10 代码。
                        • ISBN-10 到 EAN-13 的转换可以通过在数字前面添加 978 来实现。注意:978 和 979 被指定用于 ISBN 范围,在 978 用完之前不会使用 979。但是,979 不会携带 10 位对应物,因此您可以安全地通过添加 978 来构建采用 10 位 ISBN 的代码。请注意,您需要根据 GTIN-13 模数 10 规则计算新号码的校验位.
                        • 有点麻烦的方法。在我看来,使用单个循环会更好
                        猜你喜欢
                        • 1970-01-01
                        • 2017-05-20
                        • 1970-01-01
                        • 1970-01-01
                        • 2012-11-16
                        • 1970-01-01
                        • 2014-04-15
                        • 1970-01-01
                        • 1970-01-01
                        相关资源
                        最近更新 更多