【问题标题】:Calculate check digit for UPC-A barcode计算 UPC-A 条码的校验位
【发布时间】:2017-01-25 09:40:10
【问题描述】:

我目前正在 VS2010 中开发一个 Windows 窗体,该窗体应该为新部件号创建 UPC-A 并将它们存储在数据库中。到目前为止,我仍在尝试将这些部分组合在一起,并且在计算校验位时遇到了问题。我想出了以下两种情况:

场景 1:按照我们目前在 Excel 中执行的方式进行计算:

string value = uPCDataSet.UPC.Rows[uPCDataSet.UPC.Rows.Count - 1]["UPCNumber"].ToString();
string manCode = value.Substring(1, 5);
string prodCode = value.Substring(7, 5);
int first = Math.Abs(Convert.ToInt32(value));

while (first >= 10)
first /= 10;

int chkDigitSubtotal;

string UPCNumber = first + manCode + prodCode;

chkDigitSubtotal = Convert.ToInt32((UPCNumber.Substring(1, 1)) + (UPCNumber.Substring(3, 1)) + 
(UPCNumber.Substring(5, 1)) + (UPCNumber.Substring(7, 1)) + (UPCNumber.Substring(9, 1)) + 
(UPCNumber.Substring(11, 1)));

chkDigitSubtotal = (3 * chkDigitSubtotal) + Convert.ToInt32((UPCNumber.Substring(2, 1)) + 
(UPCNumber.Substring(4, 1)) + (UPCNumber.Substring(6, 1)) + (UPCNumber.Substring(8, 1)) + 
(UPCNumber.Substring(10, 1)));

 string chkDigit = ((300 - chkDigitSubtotal).ToString()).Substring(12, 1);

当我运行此代码时,我收到一个错误“对于 Int32,值太大或太小”。我不明白为什么,因为“值”是一个有 12 个字符的varchar(它们都是数字)。

场景 2:我在网上找到了一段代码。此代码有效,但给了我错误的校验位。我使用的是我们使用您的 Excel 工作表计算得出的 UPC:

string value = "606891001678"; //valid UPC with check digit
string UPCBody = value.Substring(0, 11);
int sum = 0;
bool odd = true;

for (int i = value.Length - 1; i >= 0; i--)
{
    if (odd == true)
    {
        int tSum = Convert.ToInt32(value[i].ToString()) * 2;

        if (tSum >= 10)
        {
            string tData = tSum.ToString();
            tSum = Convert.ToInt32(tData[0].ToString()) + Convert.ToInt32(tData[1].ToString());
        }
        sum += tSum;
    }
    else
        sum += Convert.ToInt32(value[i].ToString());

    odd = !odd;
}

int result = (((sum / 10)+1) *10) - sum;
result = result % 10;
MessageBox.Show(UPCBody + " & " + result);

在这个例子中,校验位显然应该是 8,但它返回 2。 我花了几乎一整天的时间,可以使用一些建议。为什么我在第一种情况下得到错误,为什么在第二种情况下我得到一个不同的校验位?

提前感谢您的帮助!

克里斯

编辑:

我删除了之前的两次编辑,因为代码存在缺陷,从某种意义上说我没有得到正确的结果。我发现我们使用 Azalea 条形码,他们有自己的计算校验位的方法(参见 ChkDigitSubtotal)。 所以下面的代码有效,并为我的场景提供了正确的校验位。我认为对于所有 UPC-A 计算,此代码将适用于 chkDigitSubtotal。感谢哈里森的出色指导和足智多谋的回答!

string value = uPCDataSet.UPC.Rows[uPCDataSet.UPC.Rows.Count - 1]
["UPCNumber"].ToString();
long chkDigitOdd;
long chkDigitEven;
long chkDigitSubtotal;
string UPCNumber = value.Substring(0, 11);
chkDigitOdd = Convert.ToInt64(UPCNumber.Substring(0, 1)) +
Convert.ToInt64(UPCNumber.Substring(2, 1)) + Convert.ToInt64(UPCNumber.Substring(4, 1)) 
+ Convert.ToInt64(UPCNumber.Substring(6, 1)) + Convert.ToInt64(UPCNumber.Substring(8, 
1)) + Convert.ToInt64(UPCNumber.Substring(10, 1));
chkDigitOdd = (3 * chkDigitOdd);
chkDigitEven = Convert.ToInt64(UPCNumber.Substring(1, 1)) + 
Convert.ToInt64(UPCNumber.Substring(3, 1)) + Convert.ToInt64(UPCNumber.Substring(5, 1)) 
+ Convert.ToInt64(UPCNumber.Substring(7, 1)) + Convert.ToInt64(UPCNumber.Substring(9, 
1));
chkDigitSubtotal = (300-(chkDigitEven + chkDigitOdd));
string chkDigit = chkDigitSubtotal.ToString();
chkDigit = chkDigit.Substring(chkDigit.Length - 1, 1);

【问题讨论】:

  • 我不知道您的问题的答案,但 C# 没有名为“varchar”的类型。这听起来像是数据库中的一种类型。 Int32 可以处理从 -2147483648 到 +2147483647 的数字,虽然它是一个 10 位数字,但不支持您需要的全部范围。添加校验位,您将需要 11 位数字。尝试使用 Decimal 类型,它支持 28 位数。
  • @Steve,varchar 确实是 SQL 数据库中“值”来自的字段类型。我会调查十进制,看看它是否能让我到任何地方。感谢您的提示!
  • 将值从数据库加载到 .NET 程序后,您就可以使用 .NET CLR 类型了。顺便说一下,这是 C# 中的值类型列表:msdn.microsoft.com/en-us/library/s1ax56ch(v=vs.100).aspx
  • 在计算 chkDigitSubtotal(在第一个示例中)时,您添加 6 个 1 位字符串并将其转换为数字。最多可能是 999,999。然后乘以 3 = 2,999,997 并再次添加一个 5 位数的字符串,最大值为 99,999 = 3099996。现在你把它从 300 = -3,099,696 中减去。我看到 8 个字符,包括减号(不包括逗号)。我不确定您将如何获取 8 个字符长的字符串的第 12 个字符?我是不是计算有误?

标签: c# visual-studio-2010 check-digit


【解决方案1】:

根据我在wikipedia 上阅读的内容,您在第一个示例中的计算不正确。

  • String.Substring + String.Subtring 或“1”+“1”=“11”。你是 寻找 1+1=2。您需要先将每个子字符串转换为 int 添加。

+ Operator (C# Reference)

二进制 + 运算符是为数字和字符串类型预定义的。为了 数字类型,+ 计算其两个操作数的和。 当一个或 两个操作数都是字符串类型,+ 连接字符串 操作数的表示。

  • String.Substring 基于 0,因此您缺少 String.Substring(0,1) 用于您的偶数位

String.Substring Method

startIndex:子字符串从零开始的起始字符位置 在这种情况下。

  • 您的校验位字符串尝试获取小于 12 位的字符串的第 12 位。此外,这不是链接中看到的计算。它正在寻找该数字的 10 - mod 10。这也发生在您更新的编辑行中

string chkDigit = ((300 - chkDigitSubtotal).ToString()).Substring(12, 1);

您正试图从第 12 个基于 0 的字符或第 13 个字符中获取长度为 1 的 Substring。但是,chkDigitSubtotal 的值不会那么大/长。

就您的错误而言,正如 Golden Dragon 在他的回答中所说,您正在将 12 个字符的字符串转换为数字,这不适合 Int32,您应该使用 long。

int first = Math.Abs(Convert.ToInt32(value));

long first = Math.Abs(Convert.ToInt64(value));

我会首先检查您的计算在前面的步骤中是否在逻辑上正常运行。

更新

在您的 edit 2 中,您的最后一行似乎将 % operator 周围的变量颠倒了。您想要将 109 除以 10(即 9)的余数,而您的代码将 10 除以 109(即 0)的余数,因此更改。

chkDigitSubtotal = 10 - (10 % (chkDigitEven + chkDigitOdd));

chkDigitSubtotal = 10 - ((chkDigitEven + chkDigitOdd) % 10);

现在 10 - 9 = 1,这是您的 checkDigit。

【讨论】:

  • 非常有帮助!感谢您的详细回答,我想我对该怎么做有了更好的了解。我更新了代码,但现在得到了一个截然不同的错误。有什么想法吗?
  • @campagnolo_1 我在答案中添加了一行,向您展示错误存在的原因。
  • 搞清楚了。感谢您的帮助!
  • @campagnolo_1 很高兴我能帮上忙。
  • 我非常感谢您不仅给了我代码,还指出了我的缺陷并给了我研究资源。干杯!
【解决方案2】:

如果 value 是一个 12 位长的字符串,那么它太大而无法放入 int。请改用long

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2021-04-29
    • 1970-01-01
    • 2013-09-14
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多