65.9K
CodeProject 正在变化。 阅读更多。
Home

C#中的IBAN验证,Excel自动化加载项,Word智能标记

starIconstarIconstarIcon
emptyStarIcon
starIcon
emptyStarIcon

3.36/5 (10投票s)

2010年1月31日

CPOL

4分钟阅读

viewsIcon

101461

downloadIcon

3531

在C#中实现IBAN验证算法,并将其用作Excel UDF和Word智能标记。

引言

在一个项目中,我需要一个IBAN(国际银行账号)验证器。我搜索了校验和计算的方法,并在IBAN维基百科条目中找到了它。我还从UN CEFACT TBG5网站上找到了一个非常好的JavaScript示例。由于我需要在C#中使用该算法,因此我根据这些网站提供的信息开发了一个C#版本。使用相同的代码,我还通过VSTO创建了一个自定义的Excel工作表函数作为自动化加载项和一个Word的智能标记

背景

IBAN在其维基百科条目中描述如下

校验和是一种基本的ISO 7064 mod 97-10计算,其中余数必须等于1。要验证校验和

  1. 根据国家/地区检查IBAN的总长度是否正确。如果不正确,则IBAN无效。
  2. 将前四个字符移到字符串的末尾。
  3. 将字符串中的每个字母替换为两个数字,从而扩展字符串,其中A=10,B=11,...,Z=35。
  4. 将字符串解释为十进制整数,并计算该数字除以97的余数。

只有当余数为1时,IBAN号才有效。

Using the Code

在C#中应用此算法时,大致如下(我承认这可能不是最佳算法)

internal static class Iban
{
    public static StatusData CheckIban(string iban, bool cleanText)
    {
        if (cleanText) // remove empty space & convert all uppercase
            iban = Regex.Replace(iban, @"\s", "").ToUpper();

        if (Regex.IsMatch(iban, @"\W"))
            return new StatusData(false, "The IBAN contains illegal characters.");

        if (!Regex.IsMatch(iban, @"^\D\D\d\d.+"))
            return new StatusData(false, "The structure of IBAN is wrong.");

        if (Regex.IsMatch(iban, @"^\D\D00.+|^\D\D01.+|^\D\D99.+"))
            return new StatusData(false, "The check digits of IBAN are wrong.");

        string countryCode = iban.Substring(0, 2);

        IbanData currentIbanData = (from id in IBANList()
                                    where id.CountryCode == countryCode
                                    select id).FirstOrDefault();

        if (currentIbanData == null)
            return new StatusData(false,
              string.Format("IBAN for country {0} currently is not avaliable.",
                            countryCode));

        if (iban.Length != currentIbanData.Lenght)
            return new StatusData(false,
              string.Format("The IBAN of {0} needs to be {1} characters long.",
                            countryCode, currentIbanData.Lenght));

        if (!Regex.IsMatch(iban.Remove(0, 4), currentIbanData.RegexStructure))
            return new StatusData(false, 
              "The country specific structure of IBAN is wrong.");

        string modifiedIban = iban.ToUpper().Substring(4) + iban.Substring(0, 4);
        modifiedIban = Regex.Replace(modifiedIban, @"\D", 
                                      m => ((int)m.Value[0] - 55).ToString());

        int remainer = 0;
        while (modifiedIban.Length >= 7)
        {
            remainer = int.Parse(remainer + modifiedIban.Substring(0, 7)) % 97;
            modifiedIban = modifiedIban.Substring(7);
        }
        remainer = int.Parse(remainer + modifiedIban) % 97;

        if (remainer != 1)
            return new StatusData(false, "The IBAN is incorrect.");


        return new StatusData(true, "The IBAN seems to be correct.");
    }


    public static List<IbanData> IBANList()
    {
        List<IbanData> newList = new List<IbanData>();
        newList.Add(new IbanData("AD", 24, 
          @"\d{8}[a-zA-Z0-9]{12}", false, 
          "AD1200012030200359100100"));
        
        //.... other countries

        newList.Add(new IbanData("TR", 26, 
          @"\d{5}[a-zA-Z0-9]{17}", false, 
          "TR330006100519786457841326"));

        return newList;
    }
}

UN CEFACT TBG5网站上提供的JavaScript在一个数组中包含了维基百科列出的所有可用国家特定数据。我决定将这些数据存储为内部List<IbanData>。由于我想向我的IbanChecker返回比true-false更多的信息,所以我还创建了一个状态数据类。

public class IbanData
{
    public string CountryCode;
    public int Lenght;
    public string RegexStructure;
    public bool IsEU924;
    public string Sample;
}
public class StatusData
{
    public bool IsValid;
    public string Message;
}

Microsoft Excel的IBAN自动化加载项

我认为如果可以在Excel工作表中找到这个检查器会非常有帮助。我可以使用VBScript的正则表达式支持(Set regex = CreateObject("VBScript.RegExp")轻松创建一个VBA解决方案。但是,我想创建一个可以部署到组织用户计算机的解决方案。Microsoft建议开发的客户端Excel UDF的方法是XLL,您必须将其开发为一种特殊的DLL,使用C API。如果您想为Excel Services(用于SharePoint)开发,已经有一个库可供您用于托管代码(Microsoft.Office.Excel.Server.Udf)。

我决定开发一个Excel自动化加载项,其中我可以使用我的托管代码作为COM互操作程序集。

public interface IExcelFunctions
{
    string CheckIban(string iban);
    bool RegexIsMatch(string cellValue, string regexPattern);
    string RegexMatchValue(string cellValue, string regexPattern);
    string[,] RegexAllMatchValues(string cellValue, string regexPattern);
}

[ComVisible(true), Guid("3....")]
[ComDefaultInterface(typeof(IExcelFunctions))]
public class ExcelFunctions : IExcelFunctions
{
    public ExcelFunctions()
    {

    }

    public string CheckIban(string iban)
    {
        StatusData status = Iban.CheckIban(iban, false);
        return status.Message;
    }

    #region COM Registration
    [ComRegisterFunctionAttribute]
    public static void RegisterFunction(Type type)
    {
        Registry.ClassesRoot.CreateSubKey(GetSubKeyName(type, "Programmable"));
        RegistryKey key = Registry.ClassesRoot.OpenSubKey(
                    GetSubKeyName(type, "InprocServer32"), true);
        key.SetValue("", System.Environment.SystemDirectory + 
                     @"\mscoree.dll", RegistryValueKind.String);
    }

    [ComUnregisterFunctionAttribute]
    public static void UnregisterFunction(Type type)
    {
        Registry.ClassesRoot.DeleteSubKey(GetSubKeyName(type, 
                             "Programmable"), false);
    }

    private static string GetSubKeyName(Type type, string subKeyName)
    {
        System.Text.StringBuilder s = new System.Text.StringBuilder();
        s.Append(@"CLSID\{");
        s.Append(type.GUID.ToString().ToUpper());
        s.Append(@"}\");
        s.Append(subKeyName);
        return s.ToString();
    }
    #endregion
}

将此托管DLL注册为COM互操作程序集后,您需要将其在Excel中激活。为此:Office按钮 -> Excel选项 -> 加载项 -> “管理:Excel加载项” 转到... -> 自动化... -> ExcelExtension.ExcelFunctions -> 确定 -> 确定。

Addins.png

AutomationServers.png

然后,您就可以从“插入函数”对话框中使用它了。

InsertFunction.png

CheckIban.png

有关开发自动化加载项的更多详细信息,您可以从Eric CarterGabhan Berry的两个博客文章开始。要将托管代码DLL注册为COM互操作程序集,您可以从Regasm.exe这篇文章(以及这个技巧)开始。如果您计划在公司范围内进行部署,您应该已经知道这一点。请参见下文获取正则表达式示例

Microsoft Word的IBAN智能标记

公司中的大部分通信都是用Word编写的。如果字母中输入的IBAN代码是正确的还是不正确的,有一个智能标记会通知您会很好。对于开发智能标记,最简单的方法是使用Office的Visual Studio工具。

public class IbanTag : SmartTag
{
    private ResourceManager rm;

    public IbanTag()
        : base("http://iban.saltug.net/iban#IbanTag", "IBAN")
    {
        Microsoft.Office.Tools.Word.Action ibanAction = 
           new Microsoft.Office.Tools.Word.Action("Validate");

        this.Expressions.Add(new System.Text.RegularExpressions.Regex(
                                 @"(?'iban'\D\D\d\d\S+)"));

        ibanAction.BeforeCaptionShow += 
          new BeforeCaptionShowEventHandler(ibanAction_BeforeCaptionShow);

        this.Actions = new Microsoft.Office.Tools.Word.Action[] { ibanAction };

        rm = new ResourceManager("IbanSmartTag.string", 
                 System.Reflection.Assembly.GetExecutingAssembly());
    }

    private void ibanAction_BeforeCaptionShow(object sender, ActionEventArgs e)
    {
        string iban = e.Properties.get_Read("iban");
        CultureInfo ci = new CultureInfo(e.Range.LanguageID.GetHashCode());

        IbanStatusData status = IbanChecker.CheckIban(iban, true, ci);

        ((Microsoft.Office.Tools.Word.Action)sender).Caption = status.IsValid 
            ? rm.GetString("IbanIsvalid", ci)
            : rm.GetString("IbanIsNotValid", ci) + 
            "-> " + status.Message;
    }
}

这是Word中的一个示例。

IbanSmartTag.png

要为Word开发智能标记,您可以阅读本教程

在IBAN自动化加载项中使用正则表达式工作表函数

在处理Excel时,特别是当它包含我需要操作的文本和数字时,我总是觉得缺少可以在工作表函数中使用的正则表达式。在开发上述Excel加载项之后,我添加了对最常用的正则表达式函数的附加功能:IsMatchMatch().Valueall Match().Value

#region Regex Functions

public bool RegexIsMatch(string cellValue, string regexPattern)
{
    return Regex.IsMatch(cellValue, regexPattern);
}
public string RegexMatchValue(string cellValue, string regexPattern)
{
    return Regex.Match(cellValue, regexPattern).Value;
}
public string[,] RegexAllMatchValues(string cellValue, string regexPattern)
{
    List resultList = new List();
    Match matchResult = Regex.Match(cellValue, regexPattern);
    while (matchResult.Success)
    {
        resultList.Add(matchResult.Value);
        matchResult = matchResult.NextMatch();
    }

    string[,] resultValues = new string[resultList.Count, 1];
    for (int i = 0; i < resultList.Count; i++)
        resultValues[i, 0] = resultList[i];

    return resultValues;
}

#endregion

示例结果

RegexIsMatch.png

RegexMatch.png

RegexAllMatchs.png

正如您所见,RegexAllMatchValues函数返回一个字符串数组。为了看到所有结果,您需要对可以容纳结果的单元格应用数组公式。您可以在RegExLib.com找到许多正则表达式示例。

C#中的IBAN验证,Excel自动化加载项,Word智能标记 - CodeProject - 代码之家
© . All rights reserved.