如何为文本文件制作自定义解析器

时间:2013-01-28 13:11:21

标签: c# asp.net regex

实际上我使用数据表设置了四列,我希望此列从文本文件中检索值。我使用正则表达式从文本文件中删除特定行。

我的目标是我想使用数据表在网格上显示文本文件,所以首先我尝试使用正则表达式创建数据表并删除行(在程序中显示)。

我在这里发布完整的代码。

namespace class
{
    public partial class Form1 : Form
    {
        public Form1()
        {
            InitializeComponent();
        }

        private void button1_Click(object sender, EventArgs e)
        { 
            StreamReader sreader = File.OpenText(@"C:\FareSearchRegex.txt");
            string line;
            DataTable dt = new DataTable();
            DataRow dr;
            dt.Columns.Add("PTC");
            dt.Columns.Add("CUR");
            dt.Columns.Add("TAX");
            dt.Columns.Add("FARE BASIS");
            while ((line = sreader.ReadLine()) != null)
            {
                var pattern = "---------- RECOMMENDATION 1 OF 3 IN GROUP 1 (USD 168.90)----------";
                var result = Regex.Replace(line,pattern," ");
                dt.Rows.Add(line);    
            }
        }
    }

    class Class1
    {
        string PTC;
        string CUR;
        float TAX;

        public string gsPTC
        {
            get{ return PTC; }
            set{ PTC = value; }
        }

        public string gsCUR
        {
            get{ return CUR; }
            set{ CUR = value; }
        }

        public float gsTAX
        {
            get{ return TAX; }
            set{ TAX = value; }
        }
    }
}

2 个答案:

答案 0 :(得分:0)

如果您的格式是严格的(例如,总是4列),并且您只想删除此完整的行,则我看不出使用正则表达式的任何理由:

var rows = File.ReadLines(@"C:\FareSearchRegex.txt")
    .Where(l => l != "---------- RECOMMENDATION 1 OF 3 IN GROUP 1 (USD 168.90)----------")
    .Select(l => new { line = l, items = l.Split(','), row = dt.Rows.Add() });
foreach (var x in rows)
    x.row.ItemArray = x.items;

(假设字段用逗号分隔)

修改:这适用于您的pastebin:

string header = "  PTC       CUR                 TAX           FARE BASIS";
bool takeNextLine = false;
foreach (string line in File.ReadLines(@"C:\FareSearchRegex.txt"))
{
    if (line.StartsWith(header))
        takeNextLine = true;
    else if (takeNextLine)
    {
        var tokens = line.Split(new[] { @"   " }, StringSplitOptions.RemoveEmptyEntries);
        dt.Rows.Add().ItemArray = tokens.Where((t, i) => i != 2).ToArray();
        takeNextLine = false;
    }
}

(因为你想要从结果中排除一个空列,我使用了笨拙且可能容易出错的(?)查询Where((t, i) => i != 2)

答案 1 :(得分:0)

要解析您需要的文件:

  1. 将文件文本拆分为数据块。在您的情况下,块可以通过标题PTC CUR TAX FARE BASISTOTAL行来标识。要分割文本,您需要将输入标记为如下> (i)定义正则表达式以匹配标题,(ii)定义正则表达式以匹配Total行(页脚);使用(i)和(ii),您可以按照出现索引的顺序加入它们,并确定每个块的总大小(请参阅下面的(x,y)=>new{StartIndex = x.Match.Index, EndIndex = y.Match.Index + y.Match.Length})行)。使用String.Substring方法分隔块。

  2. 从每个单独的块中提取数据。知道数据是按行分割的,你只需要迭代一个块中的所有行(忽略页眉和页脚)并处理每一行。

  3. 此代码应该有所帮助:

    string file = @"C:\FareSearchRegex.txt";
    string text = File.ReadAllText(file);
    var headerRegex = new Regex(@"^(\)>)?\s+PTC\s+CUR\s+TAX\s+FARE BASIS$", RegexOptions.IgnoreCase | RegexOptions.Multiline);
    var totalRegex = new Regex(@"^\s+TOTAL[\w\s.]+?$",RegexOptions.IgnoreCase | RegexOptions.Multiline);
    var lineRegex = new Regex(@"^(?<Num>\d+)?\s+(?<PTC>[A-Z]+)\s+\d+\s(?<Cur>[A-Z]{3})\s+[\d.]+\s+(?<Tax>[\d.]+)",RegexOptions.IgnoreCase | RegexOptions.Multiline);
    var dataIndices = 
        headerRegex.Matches(text).Cast<Match>()
            .Select((m, index) => new{ Index = index, Match = m })
            .Join(totalRegex.Matches(text).Cast<Match>().Select((m, index) => new{ Index = index, Match = m }),
                x => x.Index,
                x => x.Index,
                (x, y) => new{ StartIndex = x.Match.Index, EndIndex = y.Match.Index + y.Match.Length });
    var items = dataIndices
        .Aggregate(new List<string>(), (list, x) =>
        {
            var item = text.Substring(x.StartIndex, x.EndIndex - x.StartIndex);
            list.Add(item);
            return list;
        });
    
    var result = items.SelectMany(x => 
    {
        var lines = x.Split(new string[]{Environment.NewLine, "\r", "\n"}, StringSplitOptions.RemoveEmptyEntries);
        return lines.Skip(1) //Skip header
            .Take(lines.Length - 2) // Ignore footer
            .Select(line =>
            {
                var match = lineRegex.Match(line);
                return new
                {
                    Ptc = match.Groups["PTC"].Value,
                    Cur = match.Groups["Cur"].Value,
                    Tax = Convert.ToDouble(match.Groups["Tax"].Value)
                };
            });
    });