26

我需要一个 C# .NET 函数来评估键入或扫描的条形码是否是有效的全球贸易项目编号(UPC 或 EAN)。

条码校验位

条形码编号的最后一位数字是计算机校验位,可确保条形码正确组成。GTIN 校验位计算器

4

17 回答 17

26
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;
}
于 2012-04-13T15:17:50.847 回答
21

GS1 US在 PDF 文档中发布了 GTIN 的校验位计算算法(删除了不断变化的链接)。

以下代码使用 linq 检查 GTIN 条码的最后一位:GTIN-8、GTIN-12 (UPC)、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"
}
于 2013-03-10T17:53:28.850 回答
20

上面的解决方案计算校验位并将其与给定的数字进行比较,忽略了它旨在以更简单的方式进行验证的事实。

  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;
}
于 2014-01-21T20:10:07.497 回答
9

可变长度 EAN

    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

于 2013-02-24T16:02:57.463 回答
2

我找到了这个 Nuget 包:https ://www.nuget.org/packages/ProductCodeValidator/

github 中的代码是:https ://github.com/ThomasPe/ProductCodeValidator 。

以下是如何使用它:

using ProductCodeValidator;

bool IsValidEAN(string EAN)
{
    return EanValidator.IsValidEan(testEan));
}
于 2018-08-16T12:10:25.487 回答
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);
    }
于 2013-06-11T22:01:02.013 回答
1

我有一个类似的问题,谷歌把我带到了这个页面。我需要为标签生成程序计算大量条形码的校验位。我首先从上面 Luciano Carvalho 的答案的变体开始,但我对将字符串转换为 char 到 int 有点好奇。我怀疑我可能能够提高一点性能。

请注意,验证是在此函数之外进行的。由于我正在生成大量条形码,因此此功能更多地是为了速度而构建的。

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;
}
于 2014-02-14T16:33:54.933 回答
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;

    }
于 2013-08-29T13:09:27.850 回答
0

如果有人需要 AlexDev 的答案的 javascript 版本,就到这里。我注意到 AlexDev 的答案也适用于 ITF 条形码,而其他一些答案则不适用。

function isValidGtin(barcode) {
    var code = "00000" + barcode;
    code = code.substring(code.length - 14);
    var sum = 0;
    for (var i = 0; i < code.length; i++) {
        sum += code[i] * ((i % 2 == 0) ? 3 : 1);
    }
    return (sum % 10) == 0;
}
于 2021-08-09T23:16:23.740 回答
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
}
于 2015-07-14T11:02:42.400 回答
0

我编写了这个 Python 代码来快速简洁地验证 UPC 代码:

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
于 2020-01-09T20:18:01.777 回答
0

基于卢西亚诺的回答。

我删除了 Linq 依赖项,因为我在 Java 中也需要它。将填充删除为 14 位,因此可用于计算较长 ITF 条形码的校验位。

长度不等的代码由为长度不等的代码(i + code.Length % 2)制作31313图案13131的 处理。它避免了创建对象,并且需要 134 毫秒(在我的 PC 上)来计算所有 EAN-8 代码的校验位。

我知道这个问题是关于一种IsValidEan()方法的,但是如果你有一个包含方法的库,你可能无论如何都需要这个。IsValidEan()应该在长度和其他检查之后调用它进行校验位验证。

/// <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');
}

爪哇版:

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');
}
于 2019-10-19T16:52:45.573 回答
0

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

检查数字()

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

于 2016-01-20T18:09:53.613 回答
0

我还需要验证大量的 EAN-13,并在搜索时发现了这个问题。我不喜欢最受欢迎的答案的样子。处理大型数据集时可能的字符串分配过多。与仅验证每个字符是否为数字相比,正则表达式也很慢。

在我的旧笔记本电脑上运行我的 8851 个 EAN-13 数字数据集上最受好评的问题中使用的验证逻辑需要 47 毫秒,而我自己的实现只需要 2 毫秒。这里的字符串分配也少得多。

private static bool IsValidGtin(ReadOnlySpan<char> input, byte length)
{
    if (input.Length != length)
    {
        return false;
    }

    if (!char.IsDigit(input[^1]))
    {
        return false;
    }

    var sum = 0d;
    var multiplyByThree = true;
    var inputWithoutCheckDigit = input[..^1];
    for (var i = inputWithoutCheckDigit.Length - 1; i >= 0; i--)
    {
        var currentChar = inputWithoutCheckDigit[i];
        if (!char.IsDigit(currentChar))
        {
            return false;
        }

        var value = char.GetNumericValue(currentChar);
        if (multiplyByThree)
        {
            sum += value * 3;
        }
        else
        {
            sum += value;
        }

        multiplyByThree = !multiplyByThree;
    }
    
    var checkDigit = char.GetNumericValue(input[^1]);

    return (sum + checkDigit) % 10 == 0;

}

如果需要,我在这里写了更多关于它的细节

于 2021-03-07T12:24:46.300 回答
0

我今天花了一些时间来完全支持 GTIN-8、GTIN-12、GTIN-13 和 GTIN-14 的校验和计算。大多数算法示例仅支持 GTIN-13 或丑陋。

那这个呢?

public static char CalculateCheckSumDigit(string code)
{
   // https://www.gs1.org/services/how-calculate-check-digit-manually
   var reversed = code.Reverse().Skip(1);

   int sum = 0;
   int multiplier = 3;
   foreach(char c in reversed)
   {
       if (multiplier == 3)
       {
           sum += (c - 0x30) * multiplier;
           multiplier = 1;
       }
       else
       {
           sum += (c - 0x30) * multiplier;
           multiplier = 3;
       }
   }

   int closestTenth = (sum + 9) / 10 * 10;

   return (char)((closestTenth - sum) + 0x30);
}
于 2021-03-26T17:02:40.257 回答
-2

EAN8、EAN12、EAN13、EAN14 在快船/港口

function DigitEan(cCodigo)
    local cCode := AllTrim(cCodigo)
    local nLen := Len(cCode)
    local cc := 0
    local i := 0

    for i:=1 to nLen-1
        cc+=(asc(substr(cCode,nLen-i,1))-48)*iif(i%2==0,1,3)
    next

return ( (10-(cc%10))%10 )
于 2020-10-20T12:45:30.160 回答
-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;
}
于 2012-07-18T07:02:23.297 回答