1

Based on my understanding of how check digits are supposed to be calculated for barcodes, namely:

0) Sum the values of all the characters at odd indexes (1, 3, etc.)
1) Multiply that sum by 3
2) Sum the values of all the characters at even indexes (o, 2, etc.)
3) Combine the two sums from steps 1 and 2
4) Calculate the check digit by subtracting the modulus 10 of the combined sum from 10

So for example, with a barcode "04900000634" the combined sum is 40*; To get the check sum, the modulus (40 % 10) == 0, and then 10 - 0 == 10.

  • Odd characters == 7; X3 = 21; Even characters == 19, for a combined sum of 40.

Since a check digit is a scalar value, what if the result of the check digit calculation is 10? Does one use "0" or "1"?

Here is the code I'm using (thanks to some help from here: Why does 1 + 0 + 0 + 0 + 3 == 244?); I'm assuming that the formula pseudocoded above applies regardless of the length (8 chars, 12 chars, etc.) and type (128, EAN8, EAN12, etc.) of the barcode.

private void button1_Click(object sender, EventArgs e)
{
    string barcodeWithoutCzechSum = textBox1.Text.Trim();
    string czechSum = GetBarcodeChecksum(barcodeWithoutCzechSum);
    string barcodeWithCzechSum = string.Format("{0}{1}", barcodeWithoutCzechSum, czechSum);
    label1.Text = barcodeWithCzechSum;
}

public static string GetBarcodeChecksum(string barcode)
{
    int oddTotal = sumOddVals(barcode);
    int oddTotalTripled = oddTotal*3;
    int evenTotal = sumEvenVals(barcode);
    int finalTotal = oddTotalTripled + evenTotal;
    int czechSum = 10 - (finalTotal % 10);
    return czechSum.ToString();
}

private static int sumEvenVals(string barcode)
{
    int cumulativeVal = 0;
    for (int i = 0; i < barcode.Length; i++)
    {
        if (i%2 == 0)
        {
            cumulativeVal += Convert.ToInt16(barcode[i] - '0');
        }
    }
    return cumulativeVal;
}

private static int sumOddVals(string barcode)
{
    int cumulativeVal = 0;
    for (int i = 0; i < barcode.Length; i++)
    {
        if (i % 2 != 0)
        {
            cumulativeVal += Convert.ToInt16(barcode[i] - '0');
        }
    }
    return cumulativeVal;
}

UPDATE

The calculator here: http://www.gs1us.org/resources/tools/check-digit-calculator claims that the check digit for 04900000634 is 6

How is that being arrived at?

UPDATE 2

This http://www.gs1.org/barcodes/support/check_digit_calculator revises my understanding of the last part of the equation/formula, where it says, "Subtract the sum from nearest equal or higher multiple of ten = 60- 57 = 3 (Check Digit)"

So, in the case of 04900000634, the combined sum is 40. Based on that formula, the "nearest equal or higher multiple of ten" of 40 is 40, so 40-40=0, and I would expect that to be the check sum (not 6)...so, still confused...

UPDATE 3

I'm not understanding why yet, but mike z's comment must be correct, because when I reverse the "==" and "!=" logic in the sumOddVals() and sumEvenVals() functions, my results correspond to those generated by http://www.gs1us.org/resources/tools/check-digit-calculator

UPDATE 4

Apparently, based on http://en.wikipedia.org/wiki/European_Article_Number, the powers that be behind check digit calculations don't consider the first position to be position 0, but position 1. Confusing for developers, trained to see the first item as residing at index 0, not 1!

4

3 回答 3

2

校验位总是最后一个。

从校验位左边的数字开始,向左移动,对每个数字求和,交替应用 3 和 1 的权重。

然后,校验位是需要添加以产生 10 的倍数的结果的数字。

这适用于所有 EAN/UPC 代码 - UPC-E、EAN-8(除以 0,6 或 7 开头的代码外,所有有效的 8 位代码) UPC-A(12 位)、EAN-13、EAN- 14(有时称为“TUN”或“Carton”代码)和 SSCC(实际上是 18 位,但作为 EAN128 标准的一部分实施,AI 为“00”,误导一些人认为它们是 20 位代码)

引入 UPC-E 时,最初的方案是[语言][公司][产品][检查]。0,6 和 7 分配给英语,其余未分配。[company] 和 [product] 是可变长度的,共 6 位;产品多的公司短公司编号,产品少的公司长。

EAN 使用了剩余的数字,但分配了 [country][company][product][check],其中 country 是 2 位数字。

该系统很快就用完了粉扑,但偶尔仍会分配给非常小的产品 - 以及在 UPC-A/EAN-13 推出之前有编号的原始产品。

UPC-A 使用与 UPC-E 相同的模式,但丢失了对“语言”的引用。0,6 和 7 被分配到美国/加拿大。公司+产品扩展至10位。

EAN-13 将方案扩展为 13 位,2 位国家,10 位公司+产品,1 位检查。UPC-A 通过前缀“0”来兼容。

通过实施 13 位方案,美国公司可以跟踪这些代码中的每一个,并且不需要在已经分配了 EAN-13 的产品上发布 UPC-A。这计划在大约 8 年前完成,但一些公司仍然落后。

EAN-14 用于纸箱外层。前导数字通常称为“贸易单位标识符/编号”,因此整个代码有时称为 TUN。起初,有人试图编码前导数字(1=1doz、2=2doz 等),但很快就被放弃了。大多数公司使用数字作为包装级别(1=单个项目的集群,2=集群的托盘,3=托盘的盒 - 取决于每个公司的偏好。9 保留。使用 0 不是一个好主意(尽管有些公司有),因为它产生与 13 位代码相同的校验位。我已将其用于带有非零售商品批号的 EAN128 代码;AI=01;EAN-14 (=EAN13 with TUN=0) ;AI=10;批号。

SSCC 是另一种蠕虫病毒。它们是 18 位数字 - 第一个数字最初用作后勤描述符,然后是国家代码、制造商代码和带有校验位的包裹号。最初,“3”表示“外部”托盘,“4”表示“内部”托盘,但这已被废弃,因为“内部”托盘不切实际,如果发送“外部”则必须重新编号,反之亦然-反之亦然。

当然,随着越来越多的国家采用该系统,2 位数的国家代码已被 3 位数取代。

于 2013-09-12T16:49:50.487 回答
2

不同的条形码格式有不同的权重。您已经描述了 EAN 格式的格式 - 1313 加权。而 UPC 使用 3131 加权方案。ISBN-10 使用完全不同的方案 - 权重不同,计算是模 11 完成的。

我认为您使用的参考假设数字从 1 而不是 0 开始索引。效果是您混合了奇数和偶数字符。所以总和是3 x 19 + 7 = 64,因此校验位是 6 而不是 0。对于 EAN 和 UPC,校验位是必须添加到总和才能获得可被 10 整除的数字的值。

更新

您对校验位算法的描述仅对某些类别的 EAN 条码是准确的,因为权重是对齐的,因此最后一位的权重始终为 3(请参阅EAN 编号)。因此,根据确切的 EAN 方案(8、12、13 或 14 位),奇数或偶数的权重不同。

因此适当的权重是

0 4 9 0 0 0 0 0 6 3 4
3 1 3 1 3 1 3 1 3 1 3

给出 64 的总和和 6 的校验位。

于 2013-09-04T16:25:39.647 回答
1

基于此:http ://www.gs1.org/barcodes/support/check_digit_calculator ,条码计算公式可以从1开始,也可以从3开始,根据条码的最终长度是否偶数(包括校验和val ) 或添加。如果包括校验和在内的字符总数为偶数,则第 1 位的权重为 3;否则(总字符数为奇数),第一位数字的权重为 1。在任何一种情况下,3 和 1 交替出现,如“13131313...”或“31313131...”

但它们似乎总是以权重 3 结尾;所以,条形码有多长,或者它是奇数还是偶数都无关紧要。假设最后一位数字的权重为 3,只需“向后”计算值;但是,条形码的长度是偶数还是奇数,也就是说,最后一个数字和与之交替的数字是偶数还是奇数,这在世界上是完全不同的,因此也必须注意。“内部”序数从条形码中倒数第二个字符开始,向后跳过一个;“外部”序数是最后一个,然后是其他所有序数。无论如何,这是 AFAIK 应该用于生成和验证/验证所有条形码类型的校验位的代码:

private void button1_Click(object sender, EventArgs e)
{
    string barcodeWithoutCheckSum = textBox1.Text.Trim();
    string checkSum = GetBarcodeChecksum(barcodeWithoutCheckSum);
    string barcodeWithCheckSum = string.Format("{0}{1}", barcodeWithoutCheckSum, checkSum);
    label1.Text = barcodeWithCheckSum;
    textBox1.Focus();
}

public static string GetBarcodeChecksum(string barcode)
{
    int oddTotal;
    int oddTotalTripled;
    int evenTotal;
    // Which positions are odd or even depend on the length of the barcode, 
    // or more specifically, whether its length is odd or even, so:
    if (isStringOfEvenLen(barcode))
    {
        oddTotal = sumInsideOrdinals(barcode);
        oddTotalTripled = oddTotal * 3;
        evenTotal = sumOutsideOrdinals(barcode);
    }
    else
    {
        oddTotal = sumOutsideOrdinals(barcode);
        oddTotalTripled = oddTotal * 3;
        evenTotal = sumInsideOrdinals(barcode);
    }
    int finalTotal = oddTotalTripled + evenTotal;
    int modVal = finalTotal%10;
    int checkSum = 10 - modVal;
    if (checkSum == 10)
    {
        return "0";
    }
    return checkSum.ToString();
}

private static bool isStringOfEvenLen(string barcode)
{
    return (barcode.Length % 2 == 0);
}

// "EvenOrdinals" instead of "EvenVals" because values at index 0,2,4,etc. are seen by the 
// checkdigitmeisters as First, Third, Fifth, ... (etc.), not Zeroeth, Second, Fourth
private static int sumInsideOrdinals(string barcode)
{
    int cumulativeVal = 0;
    for (int i = barcode.Length-1; i > -1; i--)
    {
        if (i % 2 != 0)
        {
            cumulativeVal += Convert.ToInt16(barcode[i] - '0');
        }
    }
    return cumulativeVal;
}

// "OddOrdinals" instead of "OddVals" because values at index 1,3,5,etc. are seen by the 
// checkdigitmeisters as Second, Fourth, Sixth, ..., not First, Third, Fifth, ...
private static int sumOutsideOrdinals(string barcode)
{
    int cumulativeVal = 0;
    for (int i = barcode.Length - 1; i > -1; i--)
    {
        if (i % 2 == 0)
        {
            cumulativeVal += Convert.ToInt16(barcode[i] - '0');
        }
    }
    return cumulativeVal;
}

更新

使用上面的代码,很容易添加一个函数来验证条形码(带有附加的校验位)是否有效:

private static bool isValidBarcodeWithCheckDigit(string barcodeWithCheckDigit)
{
    string barcodeSansCheckDigit = barcodeWithCheckDigit.Substring(0, barcodeWithCheckDigit.Length - 1);
    string checkDigit = barcodeWithCheckDigit.Substring(barcodeWithCheckDigit.Length - 1, 1);
    return GetBarcodeChecksum(barcodeSansCheckDigit) == checkDigit;
}
于 2013-09-04T18:11:45.303 回答