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

使用 LINQ 验证文本文件

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.78/5 (5投票s)

2009年8月7日

CPOL

4分钟阅读

viewsIcon

27304

downloadIcon

382

本文将介绍如何基于 LINQ 验证文本文件。

UI.JPG

引言

本文将介绍如何基于 LINQ 验证文本文件。

背景

一个平面文件正被用作数据文件来传递。为了使该文件被其他系统接受,该文件必须遵循其他系统期望的数据格式。不仅是数据格式,一些业务规则也可能需要在发送文件之前进行预评估。在本文中,我将构建一个基于 LINQ 的文本文件验证引擎,以挑战该文件是否满足我为一个系统处理它而定义的所有要求。

需要验证的规则

有效文件应具有 HEADER 记录、FOOTER 记录和一些 CONTENT 记录。前 3 个字符将用于描述记录的类型。在本例中,我将使用 111 表示 HEADER 记录,999 表示 FOOTER 记录,222 表示 CONTENT 记录。我们可以使用 4-6 个字符来描述子记录信息。但是,我将仅在此处演示 HEADER、FOOTER 和 CONTENT 记录。文件示例

111this is header record
222allen.y.w#gmail.com 123-45-6789
222allen.y.w4gmail.com 123456789
222allen.y.w@gmail.com 123-45-6789
999totalrecord5

在上面的文件中,以下是我要验证的一些基本规则

  1. 文件需要有一个 HEADER 记录,该记录具有固定长度 (16) --> 如果不满足,则引发错误消息。
  2. 文件需要有一个或多个 CONTENT 记录,该记录具有固定长度 (40) --> 如果不满足,则引发错误消息。
  3. CONTENT 记录中从第 4 到 23 位的位置用于电子邮件信息,并且需要遵循电子邮件格式 (xxx@xxx.xxx) --> 如果不满足,则引发警告。
  4. CONTENT 记录中从第 24 到 40 位的位置用于 SSN 信息,并且需要像这样:xxx-xx-xxxx (x 仅为数字) -> 如果不满足,则引发警告。
  5. 文件需要有一个 FOOTER 记录。--> 如果不满足,则引发错误消息。

验证引擎类

由于验证引擎需要验证多种文本文件格式,因此我将创建一个抽象类 (BaseValidator) 来构建基本功能。一个子类 (File1Validator) 将根据其自身的规则来实现这些方法。BaseValidator 中的一个事件处理程序将在发生错误时处理传递给客户端的信息。

类图

ClassDiagram.JPG

验证引擎实现

BaseValidator

BaseValidator 是一个抽象类。它具有一个 Validate() 方法,该方法驱动所有验证过程。在本例中,当我们调用 Validate() 时,它将始终执行 ValidateHeader()ValidateContent()ValidateFooter() 过程。可以从子类重写 Validate() 方法以实现更多过程。

public virtual void Validate()
{
    ValidateHeader();
    ValidateContent();
    ValidateFooter();
}

protected abstract void ValidateHeader();
protected abstract void ValidateContent();
protected abstract void ValidateFooter();

BaseValidator 还在发生错误时创建一个事件。事件调用可以提供验证引擎和客户端之间的通信。

public event EventHandler<validatingeventargs> EventOccured;

protected virtual void OnEventOccured(ValidatingEventArgs e)
{
    EventHandler<validatingeventargs> handler = EventOccured;
    if (handler != null)
    {
        handler(this, e);
    }
}

ValidatingEventArgs 是一个对象,其中包含验证程序处理规则时的所有错误信息。由于它是一个 EventArgs 对象,因此我们可以遵循 .NET EventArgs 设计直接从 .NET EventArgs 对象继承。

namespace askbargains.com.txtValidator.Validation
{
    public class ValidatingEventArgs : EventArgs
    {
        public ValidatingEventArgs(string msg, 
               MessageType mType, RecordType rType)
        {
            this.Message = msg;
            this.MessageType = mType;
            this.RecordType = rType;
        }
        public string Message { get; set; }
        public MessageType MessageType { get; set; }
        public RecordType RecordType { get; set; }

    }
}

File1Validator

File1ValidatorBaseValidator 的一个子类。它将仅验证我上面定义的规则。可以创建更多子类来处理不同类型的文件格式。

从我们的 File1Validator,我们希望调用我们的 BaseValidator Validate() 方法来驱动基本验证流程;我们还可以在 File1ValidatorValidate() 中指定额外的验证过程。

namespace askbargains.com.txtValidator.Validation
{
    public class File1Validator: BaseValidator
    {
        //constructor
        public File1Validator(string fileLocation)
            : base(fileLocation)
        {
        }
        public override void Validate()
        {
            //triger the BaseValidator's Validate
            base.Validate();

            //adding more rules for record1 if needed
            //ex: this.ValidateAdditionalRules();
        }
    }
}

File1Validator 还处理这些 BaseValidator 抽象方法的实现。在本例中,这将是 ValidateHeader()ValidateContent()ValidateFooter()

在我们的 File1Validator 中,我们还使用 LINQ 来查询文本文件中的每种类型的记录,并使用正则表达式来处理格式。业务规则也可以以相同的方式完成。

protected override void ValidateHeader()
{
    //Select hearder rec from the file
    var heaRec = from str in RecStrs
                 where str.Substring(0, 3) == Convert.ToString((int)RecordType.HEADER)
                 select str;

    //add all the rules for header
    if (heaRec.Count() == 0)
    {
        base.OnEventOccured(new ValidatingEventArgs("File  DOES NOT " + 
             "contain Header record", MessageType.Error, RecordType.HEADER));
    }
    if (heaRec.Count() > 1)
    {
        base.OnEventOccured(new ValidatingEventArgs("Only one Header " + 
             "record allowed", MessageType.Error, RecordType.HEADER));
    }
    if (heaRec.Count() == 1)
    {
        if (heaRec.First().Length != 16)
        {
            base.OnEventOccured(new ValidatingEventArgs("Header record has " + 
                 "to be 16 charcter", MessageType.Error, RecordType.HEADER));
        }
        //add more rules for header 
    }

}

protected override void ValidateContent()
{
    //select content recs
    var conRec = from str in RecStrs
                 where str.Substring(0, 3) == Convert.ToString((int)RecordType.CONTENT)
                 select str;


    //add all the rules for content
    if (conRec.Count() == 0)
    {
        OnEventOccured(new ValidatingEventArgs("File  DOES NOT contain content record", 
                           MessageType.Error, RecordType.CONTENT));
    }

    int counter = 0;

    if (conRec.Count() > 0)
    {
        foreach (string address in conRec)
        {
            counter++;
            if (address.Length != 40)
            {
                OnEventOccured(new ValidatingEventArgs("Content record has to be 40", 
                               MessageType.Error, RecordType.CONTENT));
            }
            else
            {
                if (!email.IsMatch(address.Substring(3, 20)))
                {
                    OnEventOccured(new ValidatingEventArgs("Email is invliad " + 
                                   "for content " + counter.ToString(), 
                                   MessageType.Warning, RecordType.CONTENT));
                }

                if (!ssn.IsMatch(address.Substring(23, 11)))
                {
                    OnEventOccured(new ValidatingEventArgs("SSN is invliad " + 
                                   "fro content" + counter.ToString(), 
                                   MessageType.Warning, RecordType.CONTENT));
                }
            }

        }
    }
}

protected override void ValidateFooter()
{
    //check if file has Footer rec
    var fooRec = RecStrs
              .Where(rec => (rec.Substring(0, 3) == 
                                Convert.ToString((int)RecordType.FOOTER)))
              .Select(rec => rec.Substring(0, 3));

    if (fooRec.Count() == 0)
    {
        OnEventOccured(new ValidatingEventArgs("File  DOES NOT contain Footer Record", 
                                               MessageType.Warning, RecordType.FOOTER));
    }
}

客户端处理错误消息

在本示例中,我使用一个窗口应用程序来表示来自 File1Validator 的错误信息。由于每当捕获到错误时都会引发 ErrorOccured 事件,因此 Windows UI 将委托 EventOccured 并打印错误详细信息。

查看当我们从 UI 单击 StartValidate 时的代码

private void btnStart_Click(object sender, EventArgs e)
{
    this.listView1.Items.Clear();
    try
    {

        File1Validator recValidator = new File1Validator(textBox1.Text);
        recValidator.EventOccured += 
          new EventHandler<validatingeventargs>(recValidator_EventOccured);
        recValidator.Validate();

        MessageBox.Show("Validation completed");
    }
    catch (NullReferenceException ex)
    {
        MessageBox.Show("select an filepath", ex.Message);
    }
}

void recValidator_EventOccured(object sender, ValidatingEventArgs e)
{
    //handle error msg
    if (e.MessageType == MessageType.Error)
    {
        listView1.Items.Add(new ListViewItem(new string[] { e.MessageType.ToString(), 
           sender.ToString().Substring(sender.ToString().LastIndexOf(".") + 1), 
           e.RecordType.ToString(), e.Message }, "error.jpeg"));
    }
    //handle warning msg
    else if (e.MessageType == MessageType.Warning)
    {
        listView1.Items.Add(new ListViewItem(new string[] { e.MessageType.ToString(), 
           sender.ToString().Substring(sender.ToString().LastIndexOf(".") + 1), 
           e.RecordType.ToString(), e.Message }, "warning.jpeg"));

    }
}

Validator_EventOccured 将处理 ValidatingEventArgs 对象并在 UI 窗口中显示结果。

结论

在此应用程序中,我们构建了一个验证引擎来评估一个文本文件,以处理每一行并在不满足要求时生成错误。

© . All rights reserved.