簡體   English   中英

使用C#實現Luhn算法

[英]Implementing Luhn algorithm using C#

我正在使用以下代碼以 C# 語言實現用於信用卡檢查的Luhn 算法,但無法獲得輸出以生成其顯示有效性的校驗和。 請幫助我。 先感謝您。

public class Program
{
    private static void Main(string[]creditcard)
    {
        int sum = 0, d;
        string num ="7992739871";
        int a = 0;

        for (int i = num.Length - 2; i >= 0; i--)
        {
            d = Convert.ToInt32(num.Substring(i, 1));
            if (a % 2 == 0)
                d = d * 2;
            if (d > 9)
                d -= 9;
            sum += d;
            a++;
        }

        if ((10 - (sum % 10) == Convert.ToInt32(num.Substring(num.Length - 1))))
            Console.WriteLine("valid");

        Console.WriteLine("sum of digits of the number" + sum);
    }
}    

以下是一些計算 Luhn 校驗位、使用校驗位驗證數字以及向數字添加校驗位的擴展方法。 在 .NET 4.5 中測試。

有字符串、整數、int64s 和 IList 的擴展方法。

我從rosettacode.org得到了一些想法

using System;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;

public static class CheckDigitExtension
{
    static readonly int[] Results = { 0, 2, 4, 6, 8, 1, 3, 5, 7, 9 };

    #region extension methods for IList<int>

    /// <summary>
    /// For a list of digits, compute the ending checkdigit 
    /// </summary>
    /// <param name="digits">The list of digits for which to compute the check digit</param>
    /// <returns>the check digit</returns>
    public static int CheckDigit(this IList<int> digits)
    {
        var i = 0;
        var lengthMod = digits.Count%2;
        return (digits.Sum(d => i++ % 2 == lengthMod ? d : Results[d]) * 9) % 10;
    }

    /// <summary>
    /// Return a list of digits including the checkdigit
    /// </summary>
    /// <param name="digits">The original list of digits</param>
    /// <returns>the new list of digits including checkdigit</returns>
    public static IList<int> AppendCheckDigit(this IList<int> digits)
    {
        var result = digits;
        result.Add(digits.CheckDigit());
        return result;
    }

    /// <summary>
    /// Returns true when a list of digits has a valid checkdigit
    /// </summary>
    /// <param name="digits">The list of digits to check</param>
    /// <returns>true/false depending on valid checkdigit</returns>
    public static bool HasValidCheckDigit(this IList<int> digits)
    {
        return digits.Last() == CheckDigit(digits.Take(digits.Count - 1).ToList());
    }

    #endregion extension methods for IList<int>

    #region extension methods for strings

    /// <summary>
    /// Internal conversion function to convert string into a list of ints
    /// </summary>
    /// <param name="digits">the original string</param>
    /// <returns>the list of ints</returns>
    private static IList<int> ToDigitList(this string digits)
    {
        return digits.Select(d => d - 48).ToList();
    }

    /// <summary>
    /// For a string of digits, compute the ending checkdigit 
    /// </summary>
    /// <param name="digits">The string of digits for which to compute the check digit</param>
    /// <returns>the check digit</returns>
    public static string CheckDigit(this string digits)
    {
        return digits.ToDigitList().CheckDigit().ToString(CultureInfo.InvariantCulture);
    }

    /// <summary>
    /// Return a string of digits including the checkdigit
    /// </summary>
    /// <param name="digits">The original string of digits</param>
    /// <returns>the new string of digits including checkdigit</returns>
    public static string AppendCheckDigit(this string digits)
    {
        return digits + digits.CheckDigit(); 
    }

    /// <summary>
    /// Returns true when a string of digits has a valid checkdigit
    /// </summary>
    /// <param name="digits">The string of digits to check</param>
    /// <returns>true/false depending on valid checkdigit</returns>
    public static bool HasValidCheckDigit(this string digits)
    {
        return digits.ToDigitList().HasValidCheckDigit();
    }

    #endregion extension methods for strings

    #region extension methods for integers

    /// <summary>
    /// Internal conversion function to convert int into a list of ints, one for each digit
    /// </summary>
    /// <param name="digits">the original int</param>
    /// <returns>the list of ints</returns>
    private static IList<int> ToDigitList(this int digits)
    {
        return digits.ToString(CultureInfo.InvariantCulture).Select(d => d - 48).ToList();
    }

    /// <summary>
    /// For an integer, compute the ending checkdigit 
    /// </summary>
    /// <param name="digits">The integer for which to compute the check digit</param>
    /// <returns>the check digit</returns>
    public static int CheckDigit(this int digits)
    {
        return digits.ToDigitList().CheckDigit();
    }

    /// <summary>
    /// Return an integer including the checkdigit
    /// </summary>
    /// <param name="digits">The original integer</param>
    /// <returns>the new integer including checkdigit</returns>
    public static int AppendCheckDigit(this int digits)
    {
        return digits * 10 + digits.CheckDigit();
    }

    /// <summary>
    /// Returns true when an integer has a valid checkdigit
    /// </summary>
    /// <param name="digits">The integer to check</param>
    /// <returns>true/false depending on valid checkdigit</returns>
    public static bool HasValidCheckDigit(this int digits)
    {
        return digits.ToDigitList().HasValidCheckDigit();
    }

    #endregion extension methods for integers

    #region extension methods for int64s

    /// <summary>
    /// Internal conversion function to convert int into a list of ints, one for each digit
    /// </summary>
    /// <param name="digits">the original int</param>
    /// <returns>the list of ints</returns>
    private static IList<int> ToDigitList(this Int64 digits)
    {
        return digits.ToString(CultureInfo.InvariantCulture).Select(d => d - 48).ToList();
    }

    /// <summary>
    /// For an integer, compute the ending checkdigit 
    /// </summary>
    /// <param name="digits">The integer for which to compute the check digit</param>
    /// <returns>the check digit</returns>
    public static int CheckDigit(this Int64 digits)
    {
        return digits.ToDigitList().CheckDigit();
    }

    /// <summary>
    /// Return an integer including the checkdigit
    /// </summary>
    /// <param name="digits">The original integer</param>
    /// <returns>the new integer including checkdigit</returns>
    public static Int64 AppendCheckDigit(this Int64 digits)
    {
        return digits * 10 + digits.CheckDigit();
    }

    /// <summary>
    /// Returns true when an integer has a valid checkdigit
    /// </summary>
    /// <param name="digits">The integer to check</param>
    /// <returns>true/false depending on valid checkdigit</returns>
    public static bool HasValidCheckDigit(this Int64 digits)
    {
        return digits.ToDigitList().HasValidCheckDigit();
    }

    #endregion extension methods for int64s
}

下面是一些 XUnit 測試用例,展示了擴展方法的工作原理。

public class CheckDigitExtensionShould
{
    [Fact]
    public void ComputeCheckDigits()
    {
        Assert.Equal(0, (new List<int> { 0 }).CheckDigit());
        Assert.Equal(8, (new List<int> { 1 }).CheckDigit());
        Assert.Equal(6, (new List<int> { 2 }).CheckDigit());

        Assert.Equal(0, (new List<int> { 3, 6, 1, 5, 5 }).CheckDigit());
        Assert.Equal(0, 36155.CheckDigit());
        Assert.Equal(8, (new List<int> { 3, 6, 1, 5, 6 }).CheckDigit());
        Assert.Equal(8, 36156.CheckDigit());
        Assert.Equal(6, 36157.CheckDigit());
        Assert.Equal("6", "36157".CheckDigit());
        Assert.Equal("3", "7992739871".CheckDigit());
    }

    [Fact]
    public void ValidateCheckDigits()
    {
        Assert.True((new List<int> { 3, 6, 1, 5, 6, 8 }).HasValidCheckDigit());
        Assert.True(361568.HasValidCheckDigit());
        Assert.True("361568".HasValidCheckDigit());
        Assert.True("79927398713".HasValidCheckDigit());
    }

    [Fact]
    public void AppendCheckDigits()
    {
        Console.WriteLine("36156".CheckDigit());
        Console.WriteLine("36156".AppendCheckDigit());
        Assert.Equal("361568", "36156".AppendCheckDigit());
        Assert.Equal("79927398713", "7992739871".AppendCheckDigit());
    }
}

緊湊型 Luhn 檢查:

public static bool Luhn(string digits)
{
    return digits.All(char.IsDigit) && digits.Reverse()
        .Select(c => c - 48)
        .Select((thisNum, i) => i % 2 == 0
            ? thisNum
            :((thisNum *= 2) > 9 ? thisNum - 9 : thisNum)
        ).Sum() % 10 == 0;
}

小提琴: https : //dotnetfiddle.net/CCwE48

這是一個正確且快速的實現:

bool PassesLuhnCheck(string value)
{
    long sum = 0;

    for (int i = 0; i < value.Length; i++)
    {
        var digit = value[value.Length - 1 - i] - '0';
        sum += (i % 2 != 0) ? GetDouble(digit) : digit;
    }

    return sum % 10 == 0;

    int GetDouble(long i)
    {
        switch (i)
        {
            case 0: return 0;
            case 1: return 2;
            case 2: return 4;
            case 3: return 6;
            case 4: return 8;
            case 5: return 1;
            case 6: return 3;
            case 7: return 5;
            case 8: return 7;
            case 9: return 9;
            default: return 0;
        }
    }
}

我已經嘗試過這段代碼,它可能對未來的其他人有所幫助:

    public string GenerateLuhnNumber(string baseNumber)
    {
        if (!double.TryParse(baseNumber, out double baseNumberInt))
            throw new InvalidWorkflowException($"Field contains non-numeric character(s) : {baseNumber}");

        var step2 = string.Empty;
        for (var index = baseNumber.Length - 1; index >= 0; index -= 2)
        {
            var doubleTheValue = (int.Parse(baseNumber[index].ToString())) * 2;

            if (doubleTheValue > 9)
                doubleTheValue = Math.Abs(doubleTheValue).ToString().Sum(c => Convert.ToInt32(c.ToString()));

            step2 = step2.Insert(0, (index != 0 ? baseNumber[index - 1].ToString() : "") + doubleTheValue);
        }
        var step3 = Math.Abs(Convert.ToDouble(step2)).ToString(CultureInfo.InvariantCulture).Sum(c => Convert.ToDouble(c.ToString())).ToString(CultureInfo.InvariantCulture);

        var lastDigitStep3 = Convert.ToInt32(step3[step3.Length - 1].ToString());
        string checkDigit = "0";

        if (lastDigitStep3 != 0)
            checkDigit = (10 - lastDigitStep3).ToString();

        return baseNumber + checkDigit;
    }

你可以很簡單地做到這一點( 參考),

    public static bool Mod10Check(string creditCardNumber)
    {              
        // check whether input string is null or empty
        if (string.IsNullOrEmpty(creditCardNumber))
        {
            return false;
        }

        int sumOfDigits = creditCardNumber.Where((e) => e >= '0' && e <= '9')
                        .Reverse()
                        .Select((e, i) => ((int)e - 48) * (i % 2 == 0 ? 1 : 2))
                        .Sum((e) => e / 10 + e % 10);


        return sumOfDigits % 10 == 0;
    }

我相信這個會做到的:

static void Main(string[] args)
    {
        string number = "1762483";
        int digit = 0;
        int sum = 0;

        for (int i = 0; i <= number.Length - 1; i++)
        {

            if (i % 2 == 1)
            {
                digit = int.Parse(number.Substring(i, 1));
                sum += DoubleDigitValue(digit);

                Console.WriteLine(digit);
            }
            else
            {
                digit = int.Parse(number.Substring(i, 1));
                sum += digit;
            }

        }
        Console.WriteLine(sum);
        if (sum % 10 == 0)
        {
            Console.WriteLine("valid");
        }
        else
        {
            Console.WriteLine("Invalid");
        }
    }
    static int DoubleDigitValue(int digit)
    {
        int sum;
        int doubledDigit = digit * 2; 
        if (doubledDigit >= 10)
        {
            sum = 1 + doubledDigit % 10;
        } else
        {
            sum = doubledDigit; 
        }
        return sum; 
    }

這些是我驗證和計算最后一位數字的方法。 要驗證一個數字,只需檢查第一種方法的結果是否為 0;

private int LuhnChecksum(string input)
    {
        var length = input.Length; 
        var even = length % 2; 
        var sum = 0;

        for (var i = length - 1; i >= 0; i--)  
        {
            var d = int.Parse(input[i].ToString());
            if (i % 2 == even)
                d *= 2;
            if (d > 9)
                d -= 9;
            sum += d;
        }
        return sum % 10;
    }

    private int LuhnCalculateLastDigit(string input) 
    {
        var checksum = LuhnChecksum(input + "0");
        return checksum == 0 ? 0 : 10 - checksum;
    }
byte[] data = new byte[19];

//fill the data[] 
...

//use it
if (checkByLuhn(data))
{
  //check complete
}
...
private bool checkByLuhn(byte[] pPurposed)
{
    int nSum = 0;
    int nDigits = pPurposed.Length;
    int nParity = (nDigits - 1) % 2;
    char[] cDigit = new char[] { '0','\0' };

    for (int i = nDigits; i > 0; i--)
    {
        cDigit[0] = (char)pPurposed[i - 1];
        int nDigit = (int)Char.GetNumericValue(cDigit[0]);

        if (nParity == i % 2)
        {
            nDigit = nDigit * 2;
        }
        nSum += nDigit / 10;
        nSum += nDigit % 10;
    }
    return 0 == nSum % 10;
}

菲利普有一個很好的答案,但這里有一個更簡單的版本,仍然是 O(n)。 我已經在 xUnit 中使用 30 個數據集對其進行了測試,它比一些被贊成的答案更快。

    public static bool CheckLuhnParity(string digits)
    {
        bool isValid = false;

        if (!string.IsNullOrEmpty(digits))
        {
            long sum = 0;
            int parity = digits.Length % 2;
            for (int i = 0; i < digits.Length; i++)
            {
                int digit = digits[^(i + 1)] - '0';
                sum += (i % 2 == parity) ? Luhn(digit) : digit;
            }
            isValid = (sum % 10) == 0;
        }

        return isValid;

        int Luhn(int digit) => (digit *= 2) > 9 ? digit - 9 : digit;
    }

但是,這與公認的答案具有相同的缺陷; 它沒有完全實現Luhn 算法 它假定不需要驗證實際的校驗位,這意味着可以接受無效的數字。 這是一個更好的方法:

    public static bool CheckLuhnDigit(string digits)
    {
        bool isValid = false;

        if (!string.IsNullOrEmpty(digits) && digits.Length > 2)
        {
            long sum = 0;
            for (int i = 0; i < digits.Length - 1; i++)
            {
                int digit = digits[^(i + 2)] - '0';
                sum += (i % 2 == 0) ? Luhn(digit) : digit;
            }
            int checkDigit = digits[^1] - '0';
            isValid = (10 - (sum % 10)) % 10 == checkDigit;
        }

        return isValid;

        int Luhn(int digit) => (digit *= 2) > 9 ? digit - 9 : digit;
    }

有人想嘗試 O(log n) 嗎?

這是獲取校驗和的較短版本

private int getCheckSum(string number)
        {
            var sum = number.Reverse() //Reverse
                        .Select((d, i) => i % 2 == 0 ? Convert.ToInt32(d.ToString()) * 2 : Convert.ToInt32(d.ToString())) //double every 2nd digit including starting
                            .Select(x => x.ToString().Select(c => Convert.ToInt32(c.ToString())).Sum()) //sum double digit number 18 = 1 + 8 = 9
                              .Sum(); //Sum all
            return (10 - (sum % 10)) % 10; 
        }

要驗證校驗和,請傳遞不帶校驗和的數字,並將結果與原始數字的最后一位進行比較。

您的算法是正確的,但是您以錯誤的方式對其進行了測試。

我可以看到您的示例輸入來自 wiki 頁面: Luhn algorithm 不同之處在於,他們正在計算"7992739871X"的校驗位,其中X是他們要查找的校驗位。 您的代碼驗證您已經擁有的號碼!

將您的輸入更改為"79927398713" ,它會將其標記為正確的數字。

更新

好的,我知道問題出在哪里了。 你沒有正確對待這部分算法:

從最右邊的數字,即校驗位開始,向左移動,每第二個數字的值加倍;

您的代碼需要每隔一個數字,但沒有必要從最左邊的數字開始。 試試這個代碼:

for (int i = 0; i < num.Length; i++)
{
    d = Convert.ToInt32(num.Substring(num.Length - 1 - i, 1));
    if (a % 2 == 0)
        d = d * 2;
    if (d > 9)
        d -= 9;
    sum += d;
    a++;
}

var checkDigit = 10 - (sum % 10);

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM