2009-10-09 83 views

回答

23

看看CodeProject上的A Fast CSV Reader

+0

是的,这是一个伟大的,又名LumenWorks.Framework.IO.Csv通过Sebastien Lorien – codeulike 2009-10-09 16:18:41

26

在这里,你真的写出来使用泛型集合和迭代器块。它支持使用双转义约定的双引号括号文本字段(包括跨越多行的字段)(因此引用字段内的""读作单引号字符)。它不支持:

  • 单引号包含的文本
  • \转义引用的文字
  • 备用分隔符
  • 即开始未加引号的文本字段(将尚未在管道或制表符分隔下地干活)与报价

但是,所有这些将很容易添加,如果你需要它们。我没有在任何地方对它进行基准测试(我很想看到一些结果),但性能应该是非常好 - 比任何基于.Split()的都好。

Now on GitHub

更新:感觉就像加单引号括文本支持。这是一个简单的改变,但我将它直接输入到回复窗口中,因此未经测试。如果您喜欢旧的(测试过的)代码,请使用底部的修订链接。

public static class CSV 
{ 
    public static IEnumerable<IList<string>> FromFile(string fileName) 
    { 
     foreach (IList<string> item in FromFile(fileName, ignoreFirstLineDefault)) yield return item; 
    } 

    public static IEnumerable<IList<string>> FromFile(string fileName, bool ignoreFirstLine) 
    { 
     using (StreamReader rdr = new StreamReader(fileName)) 
     { 
      foreach(IList<string> item in FromReader(rdr, ignoreFirstLine)) yield return item; 
     } 
    } 

    public static IEnumerable<IList<string>> FromStream(Stream csv) 
    { 
     foreach (IList<string> item in FromStream(csv, ignoreFirstLineDefault)) yield return item; 
    } 

    public static IEnumerable<IList<string>> FromStream(Stream csv, bool ignoreFirstLine) 
    { 
     using (var rdr = new StreamReader(csv)) 
     { 
      foreach (IList<string> item in FromReader(rdr, ignoreFirstLine)) yield return item; 
     } 
    } 

    public static IEnumerable<IList<string>> FromReader(TextReader csv) 
    { 
     //Probably should have used TextReader instead of StreamReader 
     foreach (IList<string> item in FromReader(csv, ignoreFirstLineDefault)) yield return item; 
    } 

    public static IEnumerable<IList<string>> FromReader(TextReader csv, bool ignoreFirstLine) 
    { 
     if (ignoreFirstLine) csv.ReadLine(); 

     IList<string> result = new List<string>(); 

     StringBuilder curValue = new StringBuilder(); 
     char c; 
     c = (char)csv.Read(); 
     while (csv.Peek() != -1) 
     { 
      switch (c) 
      { 
       case ',': //empty field 
        result.Add(""); 
        c = (char)csv.Read(); 
        break; 
       case '"': //qualified text 
       case '\'': 
        char q = c; 
        c = (char)csv.Read(); 
        bool inQuotes = true; 
        while (inQuotes && csv.Peek() != -1) 
        { 
         if (c == q) 
         { 
          c = (char)csv.Read(); 
          if (c != q) 
           inQuotes = false; 
         } 

         if (inQuotes) 
         { 
          curValue.Append(c); 
          c = (char)csv.Read(); 
         } 
        } 
        result.Add(curValue.ToString()); 
        curValue = new StringBuilder(); 
        if (c == ',') c = (char)csv.Read(); // either ',', newline, or endofstream 
        break; 
       case '\n': //end of the record 
       case '\r': 
        //potential bug here depending on what your line breaks look like 
        if (result.Count > 0) // don't return empty records 
        { 
         yield return result; 
         result = new List<string>(); 
        } 
        c = (char)csv.Read(); 
        break; 
       default: //normal unqualified text 
        while (c != ',' && c != '\r' && c != '\n' && csv.Peek() != -1) 
        { 
         curValue.Append(c); 
         c = (char)csv.Read(); 
        } 
        result.Add(curValue.ToString()); 
        curValue = new StringBuilder(); 
        if (c == ',') c = (char)csv.Read(); //either ',', newline, or endofstream 
        break; 
      } 

     } 
     if (curValue.Length > 0) //potential bug: I don't want to skip on a empty column in the last record if a caller really expects it to be there 
      result.Add(curValue.ToString()); 
     if (result.Count > 0) 
      yield return result; 

    } 
    private static bool ignoreFirstLineDefault = false; 
} 
+1

它可以处理带引号的字符串中的逗号吗? “像,这个”......并且它能处理带引号的字符串中的回车符? ......这些都是一些容易引起麻烦的事情...... – codeulike 2009-10-09 16:48:28

+0

是的,它可以同时处理这两种情况。这是引用字符串的全部内容。 – 2009-10-09 16:56:09

+1

我仍然喜欢这个,但是如果我想让它结束,我可能会继承TextReader – 2009-10-09 19:05:27

8

我真的很喜欢FileHelpers库。它速度很快,它是C#100%,它可用于免费,它非常灵活和易于使用。

+1

FileHelpers向导在快速创建标准类时看起来非常有用。 – 2010-06-02 12:14:42

20

last time this question was asked,这里的the answer我给:

如果你只是想读取CSV用C#文件,最简单的办法就是使用Microsoft.VisualBasic.FileIO.TextFieldParser类。它实际上内置于.NET Framework中,而不是第三方扩展。

是的,它在Microsoft.VisualBasic.dll,但这并不意味着你不能从C#(或任何其他CLR语言)使用它。

下面是使用的例子,从MSDN documentation采取:

Using MyReader As New _ 
Microsoft.VisualBasic.FileIO.TextFieldParser("C:\testfile.txt") 
    MyReader.TextFieldType = FileIO.FieldType.Delimited 
    MyReader.SetDelimiters(",") 
    Dim currentRow As String() 
    While Not MyReader.EndOfData 
     Try 
     currentRow = MyReader.ReadFields() 
     Dim currentField As String 
     For Each currentField In currentRow 
      MsgBox(currentField) 
     Next 
     Catch ex As Microsoft.VisualBasic.FileIO.MalformedLineException 
     MsgBox("Line " & ex.Message & _ 
     "is not valid and will be skipped.") 
     End Try 
    End While 
End Using 

同样,这个例子是VB.NET,但它是微不足道将其翻译为C#。

+1

+1我不知道这门课程,但它运作得很好。 – 2011-08-05 15:11:13

+0

我已经多次使用这个类,并会推荐它。构建到.NET中意味着您不必担心授权/分发问题。 – 2014-12-22 10:23:39

3

除了解析/阅读,一些库还会做其他很好的事情,比如将解析的数据转换为对象。

下面是使用CsvHelper(我维护的库)将CSV文件读入对象的示例。

var csv = new CsvHelper(File.OpenRead("file.csv")); 
var myCustomObjectList = csv.Reader.GetRecords<MyCustomObject>(); 

默认情况下,约定用于匹配标题/列与属性。您可以通过更改设置来更改行为。

// Using attributes: 
public class MyCustomObject 
{ 
    [CsvField(Name = "First Name")] 
    public string StringProperty { get; set; } 

    [CsvField(Index = 0)] 
    public int IntProperty { get; set; } 

    [CsvField(Ignore = true)] 
    public string ShouldIgnore { get; set; } 
} 

有时候你并没有“拥有”你想用来填充数据的对象。在这种情况下,您可以使用流畅的类映射。

// Fluent class mapping: 
public sealed class MyCustomObjectMap : CsvClassMap<MyCustomObject> 
{ 
    public MyCustomObjectMap() 
    { 
     Map(m => m.StringProperty).Name("First Name"); 
     Map(m => m.IntProperty).Index(0); 
     Map(m => m.ShouldIgnore).Ignore(); 
    } 
} 
+0

我从文档中不能理解的是,什么时候使用映射,何时不应该使用映射?比方说,我只是想分析一个XML文件和额外的关键字及其附加数据。我需要使用地图吗? – 2014-05-19 15:26:19

+0

如果您正在解析XML,则不应使用CSV库来执行此操作。自2.0以来,属性映射不再存在。你使用映射文件的原因是,如果你想改变库读取文件的默认方式。映射将使您对文件的读取/写入方式有很大的控制权。 – 2014-05-19 15:32:53

+0

Opps,那是无言的......我的意思是* CSV当然。映射在.NET 4.0/5中不存在?另外,在那个笔记上,你会推荐我用什么来解析CSV并只抓取某些关键字及其数据? – 2014-05-19 16:15:52

4

我在C#中实现了Daniel Pryden的答案,所以剪切,粘贴和自定义更容易。我认为这是解析CSV文件最简单的方法。只需添加一个参考,你基本上完成了。

Microsoft.VisualBasic引用添加到您的项目

当年这里是C#示例代码乔尔的回答是:

using (Microsoft.VisualBasic.FileIO.TextFieldParser MyReader = new   
     Microsoft.VisualBasic.FileIO.TextFieldParser(filename)) 
{ 
    MyReader.TextFieldType = Microsoft.VisualBasic.FileIO.FieldType.Delimited; 
    MyReader.SetDelimiters(","); 

    while (!MyReader.EndOfData) 
    { 
     try 
     { 
      string[] fields = MyReader.ReadFields(); 
      if (first) 
      { 
       first = false; 
       continue; 
      } 

      // This is how I treat my data, you'll need to throw this out. 

      //"Type" "Post Date" "Description" "Amount" 
      LineItem li = new LineItem(); 

      li.date  = DateTime.Parse(fields[1]); 
      li.description = fields[2]; 
      li.Value  = Convert.ToDecimal(fields[3]); 

      lineitems1.Add(li); 
     } 
     catch (Microsoft.VisualBasic.FileIO.MalformedLineException ex) 
     { 
      MessageBox.Show("Line " + ex.Message + 
          " is not valid and will be skipped."); 
     } 
    } 
} 
1

您可以使用Microsoft.VisualBasic.FileIO.TextFieldParser

GET以下代码示例来自上述文章

static void Main() 
     { 
      string [email protected]"C:\Users\Administrator\Desktop\test.csv"; 

      DataTable csvData = GetDataTabletFromCSVFile(csv_file_path); 

      Console.WriteLine("Rows count:" + csvData.Rows.Count); 

      Console.ReadLine(); 
     } 


private static DataTable GetDataTabletFromCSVFile(string csv_file_path) 
     { 
      DataTable csvData = new DataTable(); 

      try 
      { 

      using(TextFieldParser csvReader = new TextFieldParser(csv_file_path)) 
       { 
        csvReader.SetDelimiters(new string[] { "," }); 
        csvReader.HasFieldsEnclosedInQuotes = true; 
        string[] colFields = csvReader.ReadFields(); 
        foreach (string column in colFields) 
        { 
         DataColumn datecolumn = new DataColumn(column); 
         datecolumn.AllowDBNull = true; 
         csvData.Columns.Add(datecolumn); 
        } 

        while (!csvReader.EndOfData) 
        { 
         string[] fieldData = csvReader.ReadFields(); 
         //Making empty value as null 
         for (int i = 0; i < fieldData.Length; i++) 
         { 
          if (fieldData[i] == "") 
          { 
           fieldData[i] = null; 
          } 
         } 
         csvData.Rows.Add(fieldData); 
        } 
       } 
      } 
      catch (Exception ex) 
      { 
      } 
      return csvData; 
     }