问题
I need a C# .NET function to evaluate whether a typed or scanned barcode is a valid Global Trade Item Number (UPC or EAN).
The last digit of a bar code number is a computer Check Digit which makes sure the bar code is correctly composed. GTIN Check Digit Calculator
回答1:
public static bool 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 8:
code = "000000" + code;
break;
case 12:
code = "00" + code;
break;
case 13:
code = "0" + code;
break;
case 14:
break;
default:
// wrong number of digits
return false;
}
// calculate check digit
int[] a = new int[13];
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] + a[11] + a[12];
int check = (10 - (sum % 10)) % 10;
// evaluate check digit
int last = int.Parse(code[13].ToString());
return check == last;
}
回答2:
GS1 US publishes the check digit calculation algorithm for GTIN in a PDF document (removed the constantly changing link).
The following code uses linq to check the last digit for GTIN barcodes: GTIN-8, GTIN-12 (UPC), GTIN-13 (EAN) and GTIN-14 (ITF-14).
private static Regex _gtinRegex = new System.Text.RegularExpressions.Regex("^(\\d{8}|\\d{12,14})$");
public static bool IsValidGtin(string code)
{
if (!(_gtinRegex.IsMatch(code))) return false; // check if all digits and with 8, 12, 13 or 14 digits
code = code.PadLeft(14, '0'); // stuff zeros at start to garantee 14 digits
int[] mult = Enumerable.Range(0, 13).Select(i => ((int)(code[i] - '0')) * ((i % 2 == 0) ? 3 : 1)).ToArray(); // STEP 1: without check digit, "Multiply value of each position" by 3 or 1
int sum = mult.Sum(); // STEP 2: "Add results together to create sum"
return (10 - (sum % 10)) % 10 == int.Parse(code[13].ToString()); // STEP 3 Equivalent to "Subtract the sum from the nearest equal or higher multiple of ten = CHECK DIGIT"
}
回答3:
The solutions above calculate the check digit and compare it to the given digit, ignoring the fact that it is designed to be validated in a much simpler way.
- Multiply all digits, including check digit, by 3 or 1 and sum.
- Check if the sum is a multiple of 10
Based on Luciano's answer:
private static Regex _gtinRegex = new Regex("^(\\d{8}|\\d{12,14})$");
public static bool IsValidGtin(string code)
{
if (!(_gtinRegex.IsMatch(code))) return false;
code = code.PadLeft(14, '0');
int sum = code.Select((c,i) => (c - '0') * ((i % 2 == 0) ? 3 : 1)).Sum();
return (sum % 10) == 0;
}
回答4:
Variable length EANs
public static bool IsValidEan13(string eanBarcode)
{
return IsValidEan(eanBarcode, 13);
}
public static bool IsValidEan12(string eanBarcode)
{
return IsValidEan(eanBarcode, 12);
}
public static bool IsValidEan14(string eanBarcode)
{
return IsValidEan(eanBarcode, 14);
}
public static bool IsValidEan8(string eanBarcode)
{
return IsValidEan(eanBarcode, 8);
}
private static bool IsValidEan(string eanBarcode, int length)
{
if (eanBarcode.Length != length) return false;
var allDigits = eanBarcode.Select(c => int.Parse(c.ToString(CultureInfo.InvariantCulture))).ToArray();
var s = length%2 == 0 ? 3 : 1;
var s2 = s == 3 ? 1 : 3;
return allDigits.Last() == (10 - (allDigits.Take(length-1).Select((c, ci) => c*(ci%2 == 0 ? s : s2)).Sum()%10))%10;
}
[Test]
[TestCaseSource("Ean_13_TestCases")]
public void Check_Ean13_Is_Valid(string ean, bool isValid)
{
BlinkBuilder.IsValidEan13(ean).Should().Be(isValid);
}
private static IEnumerable<object[]> Ean_13_TestCases()
{
yield return new object[] { "9781118143308", true };
yield return new object[] { "978111814330", false };
yield return new object[] { "97811181433081", false };
yield return new object[] { "5017188883399", true };
}
[Test]
[TestCaseSource("Ean_8_TestCases")]
public void Check_Ean8_Is_Valid(string ean, bool isValid)
{
BlinkBuilder.IsValidEan8(ean).Should().Be(isValid);
}
private static IEnumerable<object[]> Ean_8_TestCases()
{
yield return new object[] { "12345670", true };
yield return new object[] { "12345679", false };
yield return new object[] { "55432214", true };
yield return new object[] { "55432213", false };
yield return new object[] { "55432215", false };
}
EDIT
The project I was building this code for is now up and running - it's part of a comprehensive barcode database and toolset - and includes a bulk barcode validator (100 in a batch for non-reg users, 10,000 for registered) - https://blinked.in/tools/validator
回答5:
/// <summary>
/// Validates a GTIN (UPC/EAN) using the terminating check digit
/// </summary>
/// <param name="code">the string representing the GTIN</param>
/// <returns>True if the check digit matches, false if the code is not
/// parsable as a GTIN or the check digit does not match</returns>
public static bool IsValidGtin(string code)
{
if (string.IsNullOrWhiteSpace(code))
return false;
if (code.Length != 8 && code.Length != 12 && code.Length != 13
&& code.Length != 14)
// wrong number of digits
return false;
int sum = 0;
for (int i = 0; i < code.Length - 1 /* do not include check char */; i++)
{
if (!char.IsNumber(code[i]))
return false;
var cchari = (int)char.GetNumericValue(code[i]);
// even (from the right) characters get multiplied by 3
// add the length to align right
if ((code.Length + i) % 2 == 0)
sum += cchari * 3;
else
sum += cchari;
}
// validate check char
char checkChar = code[code.Length - 1];
if (!char.IsNumber(checkChar))
return false;
int checkChari = (int)char.GetNumericValue(checkChar);
return checkChari == (10 - (sum % 10)) % 10;
}
Test cases:
[TestMethod()]
public void IsValidGtinTest_Valid()
{
string[] valid = new[] {
"085126880552",
"0085126880552",
"00085126880552",
"0786936226355",
"0719852136552"
};
foreach (var upc in valid)
Assert.IsTrue(IdentifierUtilities.IsValidGtin(upc), upc);
}
[TestMethod()]
public void IsValidGtinTest_Invalid()
{
string[] invalid = new[] {
"0058126880552",
"58126880552",
"0786936223655",
"0719853136552",
"",
"00",
null,
"123456789123456789123456789",
"1111111111111"
};
foreach (var upc in invalid)
Assert.IsFalse(IdentifierUtilities.IsValidGtin(upc), upc);
}
回答6:
private bool ValidateCheckDigit()
{
Int32 _num = 0;
Int32 _checkdigit = 0;
for (int i = 0; i < CurrentUpcInfo.UpcCode.Length; i++)
{
if (i % 2 == 0)
{
_num += (3 * Convert.ToInt32(CurrentUpcInfo.UpcCode.Substring(i, 1)));
}
else
{
_num += Convert.ToInt32(CurrentUpcInfo.UpcCode.Substring(i, 1));
}
}
_num = Math.Abs(_num) + 10; // in case num is a zero
_checkdigit = (10 - (_num % 10)) % 10;
if (Convert.ToInt32(CurrentUpcInfo.Checkdigit) == _checkdigit)
return true;
return false;
}
回答7:
I had a similar problem and google brought me to this page. I needed to calculate the check digit for a large number of barcodes for a label generating program. I first started with a variation of Luciano Carvalho's answer above, but I was a little curious of the casting of the string to a char to an int. I suspected I might be able improve the performance a bit.
Please note that validating is happening outside of this function. This function is built more for speed since I am generating a large number of barcodes.
int CalculateCheckDigit(ulong label)
{
int sum = 0;
bool isEven=true;
while(label>0)
{
if(isEven)
sum += (int)(label % 10) * 3;
else
sum += (int)(label % 10) * 1;
isEven = !isEven;
label /= 10;
}
return (10 - (sum % 10)) % 10;
}
回答8:
I Found this Nuget Package: https://www.nuget.org/packages/ProductCodeValidator/
The code in github is: https://github.com/ThomasPe/ProductCodeValidator.
Here's how to use it:
using ProductCodeValidator;
bool IsValidEAN(string EAN)
{
return EanValidator.IsValidEan(testEan));
}
回答9:
I'm aware that the question is in the context of .net/C#. Nevertheless I landed on this page seeking answer to the same question, but in a Groovy context.
Since I actually succeeded in using the information on this page to come up with an answer to my own problem, I thought I'd share the result.
Especially the answers from AlexDev, Zack Peterson and Mitch was helpful to me.
/*
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 http://stackoverflow.com/questions/10143547/how-do-i-validate-a-upc-or-ean-code
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
}
回答10:
def check_digit():
users_gtin=raw_input("enter first seven digits of gtin ")
gtin_seven_digits=unicode(users_gtin)
if len(gtin_seven_digits) == 7 and gtin_seven_digits.isnumeric():
ck = ((((int(gtin_seven_digits[0])) + (int(gtin_seven_digits[2])) + (int(gtin_seven_digits[4])) + (int(gtin_seven_digits[6])))*3) + ((int(gtin_seven_digits[1])) + (int(gtin_seven_digits[3])) + (int(gtin_seven_digits[5])))) %10
final_ck = 10-ck
if final_ck == 10:
final_ck=0
print "Is your check digit",final_ck,"?"
else:
print "Is your check digit",final_ck,"?"
else:
print "please try typing an seven digit number"
check_digit()
choice=raw_input("enter (a) to restart or press anything other than the letter a to end this program <<<< ").upper()
if choice == "A":
check_digit()
check_digit()
probably not the most efficient but hope it helps..
回答11:
Based on Luciano's answer.
I removed the Linq dependency because I needed it in Java too. Removed padding to 14 digits so it may be used for calculating check digits of longer ITF barcodes.
Codes of uneven length are handled by the (i + code.Length % 2)
that makes the 31313
pattern 13131
for codes of uneven length. It avoids creating objects and takes 134 ms (on my PC) to calculate the check digits for all EAN-8 codes.
I know the question was about a IsValidEan()
method, but if you a have library with methods you would probably need this one anyway. IsValidEan()
should call this for check digit verification after length and other checks.
/// <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 version:
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');
}
回答12:
I wrote this Python code to validate UPC codes quickly and succinctly:
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
回答13:
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;
}
来源:https://stackoverflow.com/questions/10143547/how-do-i-validate-a-upc-or-ean-code