【问题标题】:Does a 2-char check digit for a barcode use the first or second char?条形码的 2 字符校验位是使用第一个字符还是第二个字符?
【发布时间】:2013-09-08 06:06:04
【问题描述】:

根据我对如何计算条形码的校验位的理解,即:

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

例如,条形码“04900000634”的总和为 40*;要获得校验和,模数 (40 % 10) == 0,然后是 10 - 0 == 10。

  • 奇数字符 == 7; X3 = 21;偶数个字符 == 19,总和为 40。

既然校验位是一个标量值,如果校验位计算的结果是10呢?使用“0”还是“1”?

这是我正在使用的代码(感谢这里的一些帮助:Why does 1 + 0 + 0 + 0 + 3 == 244?);我假设上面的伪代码公式适用于条形码的长度(8 个字符、12 个字符等)和类型(128、EAN8、EAN12 等)。

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

更新

这里的计算器:http://www.gs1us.org/resources/tools/check-digit-calculator 声称 04900000634 的校验位是 6

这是怎么得出的?

更新 2

这个http://www.gs1.org/barcodes/support/check_digit_calculator 修改了我对方程式/公式最后一部分的理解,其中说:“从最接近的等于或大于十的倍数中减去总和 = 60-57 = 3(校验位)”

因此,在 04900000634 的情况下,总和为 40。根据该公式,40 的“最接近等于或大于 10 的倍数”是 40,因此 40-40=0,我希望是校验和(不是 6)...所以,还是很困惑...

更新 3

我还不明白为什么,但 mike z 的评论一定是正确的,因为当我反转 sumOddVals() 和 sumEvenVals() 中的“==”和“!=”逻辑时函数,我的结果对应http://www.gs1us.org/resources/tools/check-digit-calculator生成的结果

更新 4

显然,基于http://en.wikipedia.org/wiki/European_Article_Number,校验位计算背后的权力不认为第一个位置是位置 0,而是位置 1。对于开发人员来说很困惑,被训练将第一个项目视为驻留在索引 0 ,而不是 1!

【问题讨论】:

  • 我已经更新了我的答案。我相信最准确的描述是权重始终对齐,最后一位数字的权重为 3。这将允许该算法适用于所有 EAN 类 - 8、12、13 或 14 位数字。
  • 谢谢;我自己也得出了这个结论。

标签: c# barcode barcode-printing


【解决方案1】:

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

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

更新

您对校验位算法的描述仅对某些类别的 EAN 条码是准确的,因为权重是对齐的,因此最后一位的权重始终为 3(请参阅EAN Number)。因此,根据具体的 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。

【讨论】:

  • 不,我从索引 0 开始。“04900000634”中的奇数位,索引 0(偶数)为 0,索引 1(奇数)为 4,等等,是“40003”,所以这些奇数的和是7;乘以 3 == 21。或者...???
  • 前导0是否被忽略?如果是这样,那就可以解释了。但是,在那种情况下,它存在的理由是什么?
  • @ClayShannon 进一步阅读 EAN check digit,权重对齐,最后一位数字的权重为 3。
【解决方案2】:

基于此:http://www.gs1.org/barcodes/support/check_digit_calculator,条码计算公式可以从1开始,也可以从3开始,根据条码的最终长度是偶数(包括校验和val)还是相加。如果包括校验和在内的字符总数为偶数,则第 1 位的权重为 3;否则(总字符数为奇数),第 1 位的权重为 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;
}

【讨论】:

    【解决方案3】:

    校验位总是最后一个。

    从校验位左边的数字开始向左移动,对每个数字求和,交替应用 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 位数取代。

    【讨论】:

    • 谢谢; Code128 是个例外,它在工作方式/捷克数字的计算方式上与其他代码完全不同。
    • 是和不是。对于 EAN128,AI=00 (SSCC)、01 或 02(产品 ID)的单个数据元素仍以相同的方式进行校验数字(以允许检查数据是否是手动键入的),但条形码本身是校验数字的不同,正如你所讨论的那样。所包含的数据和条形码之间的区别。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-04-16
    • 2018-04-23
    • 2016-03-29
    • 1970-01-01
    • 1970-01-01
    • 2015-05-16
    相关资源
    最近更新 更多