在 C# 中读取 CSV 文件

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/1544721/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-06 18:41:01  来源:igfitidea点击:

Reading CSV files in C#

c#open-sourcecsv

提问by diegocaro

Does anyone know of an open-source library that allows you to parse and read .csvfiles in C#?

有谁知道一个允许您.csv在 C# 中解析和读取文件的开源库?

采纳答案by Galwegian

Take a look at A Fast CSV Readeron CodeProject.

看看CodeProject上的快速 CSV 阅读器

回答by Joel Coehoorn

Here, written by yours truly to use generic collections and iterator blocks. It supports double-quote enclosed text fields (including ones that span mulitple lines) using the double-escaped convention (so ""inside a quoted field reads as single quote character). It does not support:

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

  • Single-quote enclosed text
  • \ -escaped quoted text
  • alternate delimiters (won't yet work on pipe or tab delimited fields)
  • Unquoted text fields that begin with a quote
  • 单引号括起来的文字
  • \ - 转义引用的文本
  • 备用分隔符(尚不适用于管道或制表符分隔的字段)
  • 以引号开头的未加引号的文本字段

But all of those would be easy enough to add if you need them. I haven't benchmarked it anywhere (I'd love to see some results), but performance should be very good- better than anything that's .Split()based anyway.

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

Now on GitHub

现在在 GitHub 上

Update: felt like adding single-quote enclosed text support. It's a simple change, but I typed it right into the reply window so it's untested. Use the revision link at the bottom if you'd prefer the old (tested) code.

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

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;
}

回答by marc_s

I really like the FileHelperslibrary. It's fast, it's C# 100%, it's available for FREE, it's very flexible and easy to use.

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

回答by Daniel Pryden

The last time this question was asked, here's the answerI gave:

上一次出现这种问题是问,这里的答案我给:

If you're just trying to read a CSV file with C#, the easiest thing is to use the Microsoft.VisualBasic.FileIO.TextFieldParserclass. It's actually built into the .NET Framework, instead of being a third-party extension.

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

Yes, it is in Microsoft.VisualBasic.dll, but that doesn't mean you can't use it from C# (or any other CLR language).

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

Here's an example of usage, taken from the MSDN documentation:

下面是一个使用示例,取自MSDN 文档

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

Again, this example is in VB.NET, but it would be trivial to translate it to C#.

同样,这个示例是在 VB.NET 中,但将其转换为 C# 是微不足道的。

回答by Josh Close

Besides parsing/reading, some libraries do other nice things like convert the parsed data into object for you.

除了解析/读取之外,一些库还会做其他一些不错的事情,例如将解析后的数据转换为对象。

Here is an example of using CsvHelper(a library I maintain) to read a CSV file into objects.

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

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

By default, conventions are used for matching the headers/columns with the properties. You can change the behavior by changing the settings.

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

// 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; }
}

Sometimes you don't "own" the object you want to populate the data with. In this case you can use fluent class mapping.

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

// 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();
    }
}

回答by Mark Lakata

I'm implementing Daniel Pryden's answer in C#, so it is easier to cut and paste and customize. I think this is the easiest method for parsing CSV files. Just add a reference and you are basically done.

我正在 C# 中实现 Daniel Pryden 的答案,因此剪切、粘贴和自定义更容易。我认为这是解析 CSV 文件的最简单方法。只需添加一个引用,您就基本完成了。

Add the Microsoft.VisualBasicReference to your project

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

Then here is sample code in C# from Joel's answer:

然后这里是 Joel 回答中的 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.");
        }
    }
}

回答by kombsh

You can use Microsoft.VisualBasic.FileIO.TextFieldParser

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

get below code example from above article

从上面的文章中获取以下代码示例

static void Main()
        {
            string csv_file_path=@"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;
        }