解析CSV格式的文本文件

时间:2010-06-20 16:14:26

标签: c# parsing

我有一个看起来像这样的文本文件:

1,Smith, 249.24, 6/10/2010
2,Johnson, 1332.23, 6/11/2010
3,Woods, 2214.22, 6/11/2010
1,Smith, 219.24, 6/11/2010

我需要能够在给定日期找到客户的余额。

我想知道我是否应该:

一个。从最后开始,将每一行读入一个数组,一次一个。    检查姓氏索引以查看它是否是我们正在寻找的客户端。    然后,显示第一场比赛的余额索引。

B中。使用RegEx查找匹配项并显示它。

我对RegEx没有多少经验,但是如果在这样的情况下,我会学到它。

7 个答案:

答案 0 :(得分:6)

我建议使用FileHelpers opensource项目: http://www.filehelpers.net/

一块蛋糕:

定义你的班级:

[DelimitedRecord(",")]
public class Customer
{
    public int CustId;

    public string Name;

    public decimal Balance;

    [FieldConverter(ConverterKind.Date, "dd-MM-yyyy")]
    public DateTime AddedDate;

}   

使用它:

var engine = new FileHelperAsyncEngine<Customer>();

// Read
using(engine.BeginReadFile("TestIn.txt"))
{
   // The engine is IEnumerable 
   foreach(Customer cust in engine)
   {
      // your code here
      Console.WriteLine(cust.Name);

      // your condition >> add balance
   }
}

答案 1 :(得分:2)

我认为最干净的方法是将整个文件加载到自定义对象数组中并使用它。对于3 MB的数据,这不会是一个问题。如果您想稍后进行完全不同的搜索,则可以重用大部分代码。我会这样做:

class Record
{
  public int Id { get; protected set; }
  public string Name { get; protected set; }
  public decimal Balance { get; protected set; }
  public DateTime Date { get; protected set; }

  public Record (int id, string name, decimal balance, DateTime date)
  {
    Id = id;
    Name = name;
    Balance = balance;
    Date = date;
  }
}

…

Record[] records = from line in File.ReadAllLines(filename)
                   let fields = line.Split(',')
                   select new Record(
                     int.Parse(fields[0]),
                     fields[1],
                     decimal.Parse(fields[2]),
                     DateTime.Parse(fields[3])
                   ).ToArray();

Record wantedRecord = records.Single
                      (r => r.Name = clientName && r.Date = givenDate);

答案 2 :(得分:2)

这看起来像一个非常标准的CSV类型布局,很容易处理。你可以用ADO.Net和Jet提供商实际做到这一点,但我认为从长远来看它可能更容易自己处理它。

首先,您要处理实际的文本数据。我假设假设每条记录都被一些换行符分隔是合理的,所以你可以利用ReadLine方法轻松获取每条记录:

StreamReader reader = new StreamReader("C:\Path\To\file.txt")
while(true)
{
    var line = reader.ReadLine();
    if(string.IsNullOrEmpty(line))
        break;
    // Process Line
}

然后要处理每一行,您可以在逗号上拆分字符串,并将值存储到数据结构中。因此,如果您使用这样的数据结构:

public class MyData
{
    public int Id { get; set; }
    public string Name { get; set; }
    public decimal Balance { get; set; }
    public DateTime Date { get; set; }
}

您可以使用以下方法处理行数据:

public MyData GetRecord(string line)
{
    var fields = line.Split(',');
    return new MyData()
    {
        Id = int.Parse(fields[0]),
        Name = fields[1],
        Balance = decimal.Parse(fields[2]),
        Date = DateTime.Parse(fields[3])
    };
}

现在,这是最简单的示例,并不考虑字段可能为空的情况,在这种情况下,您需要为这些字段支持NULL(使用可空类型int?,decimal?和DateTime? ),或定义一些将分配给这些值的默认值。

所以,一旦你有了,你可以将MyData对象的集合存储在一个列表中,并根据它轻松执行计算。因此,假设您在给定日期找到余额的示例,您可以执行以下操作:

var data = customerDataList.First(d => d.Name == customerNameImLookingFor 
                                    && d.Date == dateImLookingFor);

其中customerDataList是从文件中读取的MyData个对象的集合,customerNameImLookingFor是包含客户名称的变量,customerDateImLookingFor是包含日期的变量。

我已经使用这种技术处理过去文本文件中的数据,用于从几条记录到数万条记录的文件,并且它运行良好。

答案 3 :(得分:1)

请注意,您的选项都将扫描文件。如果您只想在文件中搜索1个项目,那就没问题了。

如果您需要在同一文件中搜索多个客户端/日期组合,则可以先将文件解析为Dictionary<string, Dictionary <date, decimal>>

直接回答:对于一次性,RegEx可能会更快。

答案 4 :(得分:1)

如果您只是阅读它,我会考虑使用StreamReader.ReadToEnd在内存中读取整个文件,然后将其视为一个长字符串进行搜索,当您找到想要查看的记录时查找上一个和下一个换行符,然后找到所需的事务行。

如果它在服务器上或者文件可以一直刷新,这可能不是一个好的解决方案。

答案 5 :(得分:1)

如果它像这样格式良好的CSV,那么我会在代码项目中使用类似Microsoft.VisualBasic.TextFieldParser类或Fast CSV类的内容来读取它。

数据类型有点棘手,因为我想不是每个客户每天都有记录。这意味着您不能只为您的查找设置嵌套字典。相反,您希望首先按名称“索引”,然后按日期,但日期记录的形式稍有不同。我想我会在每条记录中读到这样的东西:

Dictionary<string, SortedList<DateTime, double>>

答案 6 :(得分:1)

嘿,嘿,嘿!!!为什么不在codeproject Linq to CSV上使用这个伟大的项目,这很酷! 坚如磐石