ICCID在使用的時候,有的電訊廠商使用18位,有的電訊廠商使用19位(ICCID-C包含一個校正位)。那麼ICCID-C的校正位的演算法通行是採用Luhn演算法。這個演算法只是用於對數字串的輸入錯誤校正,而非安全性檢查。
由於我們的系統中是使用18位的ICCID,為了在Portability系統中與其他電訊廠商相相容,我們將外部傳入的19位的ICC做去末位處理,對於外部系統所需要的19位ICCID-C,我們直接根據Luhn演算法計算出去校正位,加到18位的ICCID後面,返回給客戶系統。
Luhn演算法,由於其目的是為了檢查輸入錯誤,所以該演算法只要能夠做到一個數字串中的某一位元字輸入錯誤,則其校正位不同,那麼校正的功能就能夠實現了。
Luhn演算法的基本描述:從低位(右向左)起,索引為偶數的數字乘以一個值,索引為奇數的數字則保留不變,經過如此處理後,將所有位對應的數值取數字和#sum。如果#sum以0為結尾,則校正位就為0,否則,以模的值減去#sum的末位元字,便為校正值。
Luhn演算法一般隔一行做乘積處理,而且乘數的選擇有一個講究的地方,那就是0-9這10個數字乘以該乘數的結果的數字和不能存在重複的情況。例如,乘數為3,那麼2*3=6(數字和為6),8*3=24(數字和也為6),那麼如果把2誤敲為8,就檢查不出來了,而一般採用倍乘,即乘數為2,2就不會出現這樣的問題。
此外,在模數的時候,也有多種選擇,一般模數10,比較簡單,關於模數取10以外的其他值,也會存在類似於乘數的選擇所帶來的問題,而且模10的使用在兩處都有涉及,一個是數字和,另一個是用模減去數字和的末位元字,模不同,數字和就有可能不同,最終計算出來的校正位也有可能不同,不過這個我沒有做深入研究。這裡面有一個最佳化選擇問題,一般選擇模10隔位2倍加的演算法,這應該是實踐得出的一個較優選擇。
下面是Luhn的演算法的c#實現,該演算法非常簡單,在網上能找到該演算法的樣本描述,c#實現的也找到了一個,但是感覺有點複雜,寫得不太好。我在實現的時候,也固定了模10隔位2倍加,沒有在模和隔位元和倍數上預留指定的空間。
下面是我的實現。
Code
public class Luhn
{
/**//// <summary>
/// Luhn計算模10“隔位2倍加”校正數字
/// </summary>
/// <param name="numberString">numberString</param>
/// <returns></returns>
public int GetCheckDigit(string numberString)
{
bool valid = isValidNumberString(numberString);
if (valid == false) throw new ArgumentException("Invalid parameter.","numberString");
int sum = getMod10Compartment2Sum(numberString);
return sum%10 == 0 ? 0 : 10 - sum%10;
}
/**//// <summary>
/// 按 Luhn計算模10“隔位2倍加”的演算法來檢驗一個數字串的正確性
/// </summary>
/// <param name="numberStringWithCheckDigit">numberStringWithCheckDigit</param>
/// <returns></returns>
public bool IsValid(string numberStringWithCheckDigit)
{
bool valid = isValidNumberString(numberStringWithCheckDigit);
if (valid == false) throw new ArgumentException("Invalid parameter.", "numberStringWithCheckDigit");
int checkDigit =
GetCheckDigit(numberStringWithCheckDigit.Substring(0, numberStringWithCheckDigit.Length - 1));
int lastDigit =
Convert.ToInt16(numberStringWithCheckDigit[numberStringWithCheckDigit.Length - 1].ToString());
return lastDigit == checkDigit;
}
/**//// <summary>
/// 計算數值的數字和
/// </summary>
/// <param name="number"></param>
/// <returns></returns>
private static int getDigitsSum(int number)
{
int sum = 0;
for (int i = 0; i < number.ToString().Length; i++ )
{
sum += Convert.ToInt16(number.ToString()[i].ToString());
}
return sum;
}
/**//// <summary>
/// 計算模10“隔位2倍加”的和
/// </summary>
/// <param name="numberString">numberString</param>
/// <returns></returns>
private static int getMod10Compartment2Sum(string numberString)
{
int sum = 0;
for (int i = 0; i < numberString.Length; i++)
{
int index = numberString.Length - i - 1;
if (i % 2 == 1) sum += Convert.ToInt16(numberString[index].ToString());
else sum += getDigitsSum(Convert.ToInt16(numberString[index].ToString()) * 2);
}
return sum;
}
/**//// <summary>
/// 檢查輸入的數字串是否合法
/// </summary>
/// <param name="numberString">numberString</param>
/// <returns></returns>
private static bool isValidNumberString(string numberString)
{
if(string.IsNullOrEmpty( numberString ))
{
return false;
}
string regextext = @"^[0-9]+$";
Regex regex = new Regex(regextext, RegexOptions.None);
return regex.IsMatch(numberString.Trim());
}
}