2012-04-13 117 views
16

我需要一個C#.NET函數來評估打印或掃描的條形碼是否是有效的Global Trade Item Number(UPC或EAN)。如何驗證UPC或EAN代碼?

barcode check digit

條形碼編號的最後一位是計算機校驗位,這使得確定條形碼是否正確組成。 GTIN Check Digit Calculator

+2

@Zack我假設你現在有一個答案,但我想指出,如果您的系統打算處理ISBN-10代碼(隨着較舊的書籍從市場上銷燬,這些代碼最終會消失),您需要爲此包括一張支票。您的問題僅限於GTIN,但ISBN-10可以轉換爲與EAN/GTIN-13等效的ISBN-13。原因:ISBN-10是模11,因此使用字母'X'作爲可能的校驗位來表示數字10.只有在您查找數字時纔會失敗,除非您先將其轉換爲ISBN-13。 – 2013-04-25 11:15:13

回答

19
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; 
} 
+0

右邊,UPC代碼中的最後一位是一個「模數檢查」數字。在Petzold的書_Code_中可以找到對此的真正平易近人的解釋。 – ybakos 2012-04-13 15:22:26

+0

我希望您不打算掃描此係統上的ISBN-10條形碼(書籍在ISBN-13之前過去使用過)。 ISBN-10以11爲模。我可以用一個字母'X'作爲校驗位來表示數字10.可能的解決方案:首先轉換爲ISBN-13(相當於EAN-13/GTIN-13)。 – 2013-04-25 11:23:59

+0

注意:ISBN-10由ISBN-13於2007年1月1日替換。這並不意味着當前在書架上的書籍不會帶有兩個代碼(向後兼容)。如果這個系統有一個人的輸入界面,並有任何處理書籍的機會,你會想要防止他們挑選ISBN-10代碼。 – 2013-04-25 11:35:57

-3
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; 
} 
+4

親愛的上帝,這是什麼憎惡...... – surjikal 2013-02-06 00:24:03

+1

這段代碼是一個非常糟糕的方法來解決問題。它只能在程序員無法訪問循環結構時出於某種原因才能使用。無論如何,評論「用零填充以延長到14位數」與填充到11的代碼不一致,並且大部分EAN代碼預計有13位數。我不知道@Adi Lester編輯過的內容,但是這段代碼並沒有提供正確的解決方案。 – ThunderGr 2013-09-26 06:20:45

+0

@ThunderGr我只是修復了答案的格式。點擊「編輯...」鏈接即可查看修訂歷史記錄 – 2013-09-26 09:53:18

5

可變長度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 }; 
    } 

編輯

的項目,我建立這個代碼是現在運行起來 - 這是一個全面的條形碼數據庫的一部分,工具集 - 幷包含批量條形碼驗證程序(非註冊用戶批量處理100個,註冊10,000個) - https://blinked.in/tools/validator

17

GS1 US發佈GTIN的校驗位計算算法 PDF文件(刪除不斷變化的鏈接)。 GTIN-8(GTIN-12),GTIN-13(EAN)和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" 
} 
+0

將解析解除爲int,實際上我沒有將其用於生產代碼。參考:http://stackoverflow.com/questions/3665757/c-sharp-convert-char-to-int – 2014-09-07 21:07:38

+0

非常簡潔。但是,通過用'((int)char.GetNumericValue(code [i]))'替換'((int)(code [i] - '0'))'',可以使它更容易被讀取。 – NightOwl888 2016-03-16 18:36:04

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

測試用例:

[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); 
    } 
1
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; 

    } 
9

上述的方案計算校驗位,並將其與給定的數字,忽略了一個事實,它被設計成以進行驗證更簡單的方法。

  1. 乘法所有數字,包括校驗位,由3或1和總和。
  2. 檢查總和是基於盧西亞諾的回答10

的倍數:

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

我有一個類似的問題,谷歌把我帶到這個頁面。 我需要爲標籤生成程序計算大量條形碼的校驗位。我首先從上面的Luciano Carvalho的回答開始,但我對將字符串轉換爲char類型有點好奇。我懷疑我可能會提高性能。

請注意驗證發生在此功能之外。由於我生成大量條形碼,因此此功能的速度更快。

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

我實際在我的代碼中使用的避免解析的解決方案是用於char c,(c - '0'),就像在http:// stackoverflow中一樣。com/questions/3665757/c-sharp-convert-char-to-int – 2014-09-07 21:04:51

0

我知道問題是在.net/C#的上下文中。儘管如此,我在這個頁面登陸尋求同一個問題的答案,但在Groovy環境下。

由於我實際上已經成功地使用本頁上的信息來解答我自己的問題,所以我想我會分享結果。
特別是AlexDev,Zack Peterson和Mitch的回答對我很有幫助。

/* 
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 
} 
0

高清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()

可能不是最有效的,但希望它有助於..

+0

如果你解釋一下你的答案,可能會有所幫助,而不是僅僅編寫代碼,這樣他們就可以學習 – winhowes 2016-01-20 18:28:45

+0

抱歉,真的知道如何添加評論,我發現使用#它使代碼看起來更復雜,更難理解,但不知道如何添加描述@winhowes – 2016-11-15 21:52:16