如何验证UPC或EAN码?

29

2
@Zack 我假设你现在已经有了答案,但我想指出,如果你的系统打算处理ISBN-10代码(随着旧书下架,这些代码最终会消失),你需要包括一个检查。虽然你的问题是关于GTIN的,但ISBN-10可以转换为ISBN-13,这相当于EAN / GTIN-13。为什么:ISBN-10是模11,因此使用字母“X”作为可能的校验位来表示数字10。只寻找数字会失败,除非你先转换为ISBN-13。 - Zack Jannsen
仅供日后参考,该算法的描述在此处。 - jeroenh
17个回答

0
如果有人需要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;
}

0
今天我花了一些时间来完全支持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);
}

0

我知道这个问题是在 .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 https://dev59.com/Dmkw5IYBdhLWcg3wGGq8
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
}

0
我写了这段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

0

基于Luciano的答案。

我移除了Linq依赖,因为我也需要在Java中使用它。将填充位数减少到14位,以便用于计算更长的ITF条码的校验位。

通过使用(i + code.Length % 2)处理不均匀长度的代码,使得31313模式对于不均匀长度的代码变为13131。这样可以避免创建对象,并且在我的电脑上计算所有EAN-8代码的校验位只需134毫秒。

我知道问题是关于一个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');
}

-2
在Clipper/Harbour中处理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 )

1
请将以下与编程有关的内容从英语翻译为中文。仅返回翻译后的文本,不要进行解释。 - General Grievance
这个问题已经有多种语言的答案了。如果它展示了不同的方法或算法,即使语法不同,仍然可以增加价值。这个答案唯一的问题是没有解释这里发生了什么或为什么。仅有代码还不够。 - Mnebuerquo

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

2
这段代码是解决问题的非常糟糕的方法。只有在某些情况下程序员无法访问循环结构时才能使用它。注释“用零填充以将长度延长到14位数”与填充到11位数的代码不一致,而且EAN码的大部分预期都有13位数。我不知道@Adi Lester编辑了什么,但这段代码并没有提供正确的解决方案。 - ThunderGr
@ThunderGr 我只是修复了答案的格式。您可以通过点击“编辑...”链接查看修订历史记录。 - Adi Lester

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接