有许多条形码类型和尺寸(长度)。是否有一组通用的算法可用于计算任何条形码的校验和?
答案 0 :(得分:3)
是的,有一种非常常见的校验和计算器算法。各种条形码(和其他数字输入方案)使用它们来验证扫描仪(或人)正确输入所有数字。第一个例子之一,最常用的校验和算法是Luhn算法,并用于信用卡,但存在许多变化。但是,大多数人使用相同的算法。
通用算法使用对应于数字位置的权重数组,模除数和指示"产品添加"或"产品数字添加"方案。
int:computeChecksum(string:inputData, int[len(inputData]: weightArray, int:divisor, Boolean:productDigitAdd)
{
If the number of digits in inputData is not equal to the number of elements in weightArray
raise an invalidWeightArray exception
endif
create an int:checksum and set it to zero
For int:position = each digit in inputData
int:digitProduct = value of inputData digit at [position] times the weightArray at [position]
If productDigitAdd then
for int:prodPosition = each digit in digitProduct
checksum = checksum + digitProduct[prodPosition]
end for
else
checksum = checksum + digitProduct
end if
end for
int:remainder = checksum modulo divided by divisor
return remainder
}
检查数字方案在各个行业中都很常用,条形码生产和验证只是其中之一。以下是可与此算法一起使用的一些常用校验位方案的列表:
要使用此算法验证条形码,只需将其输出与零进行比较即可。非零值表示失败。
常见的错误是尝试隔离校验位,然后将例程的输出与隔离的校验位进行比较。简单地在整个循环中包含校验位然后将输出与零进行比较会更简单,更安全。然后,该算法继续用于嵌入在条形码右端以外的位置的校验位,或校验位具有非一个权重的位置。
您也可以使用相同的算法来计算校验位,就像生成新条形码时一样。在输入数据中,其中一个数字将保留用于校验位的位置。这通常是,但不总是最右边的数字。将输入数字数组的位置设置为零。像验证inputData那样调用算法,然后从模除数中减去算法的输出。用减去的输出替换输入数字数组中的零占位符。
请注意,对于ISBN号,除数为11的情况下,这可以产生" 10"作为输出数字。但是两位数值不适合保留给一位数的条形码位置。 ISBN规范说10应该用字母X代替。我遇到的其他方案只是丢弃,因为任何产生两位数校验位数字的数字都是不可能的。
对于此类或其他使用非数字值的方案,最好的方法是将此例程更改为接受"字符串"作为输入数据,而是将其转换为接受输入值数组。然后在此例程之前和之后执行转换步骤,以将给定符号映射到必要值。通常我会将此例程的一个版本专门用于接受输入的字符串类型,因为大多数程序将条形码作为字符串处理。
一种非常常见的优化,特别是在条形码扫描仪等嵌入式设备中,是接受一个比数字位数短的权重数组,并将数据的位数与数字中的数字一样多。这样,所有UPC和EAN方案,包括UPC-A,UPC-E,EAN-8和EAN-13,都与常规例程相匹配。然后权重为{3,1},除数为10,PDA为假。必须通过将最右边的权重数字锚定到最右边的inputData数字来扩展权重,因此31的权重变为
<-31
1313131313131
9780321146533
相同的技术处理任何重复的权重集。对于使用21重量的信用卡的Luhn算法,相同的例程适用于13位Visa卡,15位AmEx卡和16位Visa卡。但它需要外部验证数字的长度,因此它不会节省太多。
答案 1 :(得分:0)
条形码尽管长度不同且具有不同的语义,但仍然遵循生成校验和的一些基本规则。下面的代码可以复制到一个小的util中进行测试(或者只是使用非事件处理代码):
要使用它,请创建一个WinForms应用程序,并在其上删除以下控件:
0) A button named button1; give it text something like "Calculate barcode and append it to label below", if desired
1) A button named button2; give it text something like "Valid barcode + czech digit", if desired
2) A label named label1, which displays the result of clicking button1
3) A textBox named textBox1, wherein you enter a raw barcode value (sans check digit) prior to clicking button1 OR enter a full barcode value (with check digit) prior to clicking button2
// "Calculate check sum" handler
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();
}
// Verify/validate existing checksum handler
private void button2_Click(object sender, EventArgs e)
{
string bcVal = textBox1.Text.Trim();
bool validCheckDigit = isValidBarcodeWithCheckDigit(bcVal);
MessageBox.Show(validCheckDigit ? string.Format("{0} is valid", bcVal) : string.Format("{0} invalid", bcVal));
}
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);
//MessageBox.Show(string.Format("raw barcode portion is {0}", barcodeSansCheckDigit));
//MessageBox.Show(string.Format("check portion is {0}", checkDigit));
return GetBarcodeChecksum(barcodeSansCheckDigit) == checkDigit;
}