我正在尝试读取*.csv-文件。

*.csv-文件由两列组成,两列之间用分号(“;”)分隔。

我能够使用StreamReader读取*.csv-文件,并能够使用Split()函数分隔每一行。我想将每一列存储到一个单独的数组中,然后显示它。

可以做到吗?

评论

@Marc:不幸的是,在非英语文化(例如意大利语)中,当您将excel保存为CSV时,它会使用“;”作为分隔符...这使CSV成为非标准imo了:(

我总是将CSV视为字符分隔值,因为人们会调用CSV文件,即使他们不使用逗号作为分隔符也是如此。实际上,有许多方言使用不同的引号或转义规则,即使在理论上存在RFC,您也无法真正谈论标准。

CSV文件扩展名现在应该更改为DSV-分隔符分隔值文件

对于所有将分隔符上的字符串简单分割的答案,这都不是最好的方法。 CSV格式还有更多规则,这些将不涉及。最好使用第三方解析器。更多信息-dotnetcoretutorials.com/2018/08/04/csv-parsing-in-net-core

#1 楼

您可以这样操作:

using System.IO;

static void Main(string[] args)
{
    using(var reader = new StreamReader(@"C:\test.csv"))
    {
        List<string> listA = new List<string>();
        List<string> listB = new List<string>();
        while (!reader.EndOfStream)
        {
            var line = reader.ReadLine();
            var values = line.Split(';');

            listA.Add(values[0]);
            listB.Add(values[1]);
        }
    }
}


评论


为此,我忘记了如何在csv文件中分割行(愚蠢的我!),但是您的解决方案帮助了我:)

–哈拉甘(Hallaghan)
2011-09-15 15:58

三年多了,这个问题仍然在帮助某人。我很遗憾您没有对此接受。

– AdamMc331
2014年8月12日17:50

不使用逗号等处理字段值。

–迈克
14年8月26日在18:39

应该在这里使用using子句,或者至少手动使用Close()读者,因为它是IDisposible资源。

–阿萨夫(以色列)
2015年5月29日在6:16

这也不会正确解析像column1;“ Special; char in string”; column3这样写的CSV-tools.ietf.org/html/rfc4180

– Ole K
2015年10月23日14:55



#2 楼

我最喜欢的CSV解析器是.NET库中内置的解析器。这是Microsoft.VisualBasic命名空间中的隐藏财富。
下面是示例代码:

/>此处提供了有关解析器的更多详细信息:http://codeskaters.blogspot.ae/2015/11/c-easiest-csv-parser-built-in-net.html

评论


我最喜欢这个选项。我不必担心转义字符,因为该类是CSV解析器,而不是手动构建的东西。

–提莫西·冈萨雷斯(Timothy Gonzalez)
16-10-10在19:15

如果有人遇到这个问题并想知道,您将需要包括对Microsoft.VisualBasic框架程序集的引用,因为默认情况下通常不引用该程序集。

–apokryfos
17年2月21日在15:45

我希望我在VB6的日子里记得这一点,多年来可以为我节省很多时间。尽管有些人会抱怨VB,但如果它具有价值,我将dll和命名空间添加到我的代码中就没有问题。这具有很多价值。

–沃尔特
18年5月30日在15:56

该解决方案是本垒打。根据我的经验,它是非常可靠的解析器。

– Glenn Ferrie
19年3月28日在19:12

为什么只在VB dll中?

–马克·崔(Mark Choi)
19/12/27在7:53

#3 楼

LINQ方式:

var lines = File.ReadAllLines("test.txt").Select(a => a.Split(';'));
var csv = from line in lines
          select (from piece in line
                  select piece);


^^错误-由尼克编辑-包含数组的数组。第一个数组中的每个项目都包含一个表示该行号的数组,而嵌套数组中的每个项目都包含该特定列的数据。

var csv = from line in lines
          select (line.Split(',')).ToArray();


评论


@ClayShannon .NET 1.1吗?我...很抱歉

– contactmatt
13年6月3日在17:55

@contactmatt:我不会拒绝你的那种情绪。

– B.克莱·香农
2013年6月3日19:01

我还想指出的是,csv可以用引号引起来...所以使用string.Split不是一个可行的选择。

– Alxandr
13年7月9日在10:48

我得到:'System.Array'不包含'Split'的定义,找不到扩展方法'Split'接受类型为'System.Array'的第一个参数(您是否缺少using指令或程序集引用?)

–卡拉J
2014年5月16日19:22

您正在获取System.Array不包含定义,因为lines是IEnumerable(string []),因此line本质上是具有一个元素的字符串数组。只需将其更改为var csv =从行中选择(line [0] .Split(','))。ToArray();

– Zein Sleiman
2014年6月13日在22:27



#4 楼

您无法立即创建数组,因为您需要从头开始就知道行数(这将需要两次读取csv文件)

您可以将值存储在两个List<T>中,然后使用它们或使用List<T>.ToArray()转换为数组非常简单的示例:

var column1 = new List<string>();
var column2 = new List<string>();
using (var rd = new StreamReader("filename.csv"))
{
    while (!rd.EndOfStream)
    {
        var splits = rd.ReadLine().Split(';');
        column1.Add(splits[0]);
        column2.Add(splits[1]);
    }
}
// print column1
Console.WriteLine("Column 1:");
foreach (var element in column1)
    Console.WriteLine(element);

// print column2
Console.WriteLine("Column 2:");
foreach (var element in column2)
    Console.WriteLine(element);


NB

请注意只是一个非常简单的例子。使用string.Split不能解决某些记录在其中包含分隔符;的情况。

评论


不占;是值的一部分,例如“ value with with; inside”。包含特殊字符并带有双引号的CSV环绕值表示它是文字字符串。

–ChickenFeet
18/12/12在8:19



@ChickenFeet:当然,这就是标题的原因:“非常简单的例子”。无论如何,我可以添加有关的注释;)

–digEmAll
18/12/12在8:39

不用担心,我注意到这里还有很多其他答案也没有说明:)

–ChickenFeet
18年12月12日9:00

Regex.Split(sr.ReadLine(),“,(?=(?:[^ \”] * \“ [^ \”] * \“)* [^ \”] * $)“); //找到这在SO ...比库快。

–捏
19年8月7日在22:15



#5 楼

刚遇到这个库:https://github.com/JoshClose/CsvHelper

非常直观且易于使用。也有一个可以快速实现的nuget包:http://nuget.org/packages/CsvHelper/1.17.0。我也很喜欢维护它。

配置它使用分号很容易:https://github.com/JoshClose/CsvHelper/wiki/Custom-Configurations

评论


这是最好的答案!健壮的库,易于插入和滚动。

–泰勒·福赛斯(Tyler Forsythe)
2014年5月27日23:39

CsvHelper库很棒。超级快速且易于使用。

–史蒂夫教区
2014年9月8日上午9:45

如果您正在寻找一种可以处理csv格式各个方面(包括带引号的字符串)的库,请使用此库。太棒了!

–马特
2015年4月1日在6:41

Thx,非常漂亮的库,易于使用且非常强大。

–塞巴斯蒂安·格雷罗(SebastiánGuerrero)
16年6月28日在20:44

性能与Microsoft.VisualBasic.FileIO.TextFieldParser相比如何(请参阅@Habeeb的答案)?

– bovender
16年11月7日在5:39

#6 楼

我通常使用来自codeproject的解析器,因为有很多字符转义符和类似的字符转义符可以为我处理。

评论


这个东西非常好而且很快。如果您处在业务状况中并且需要破解,请使用此功能。

– gjvdkamp
2011年3月12日下午14:45

如果您不想注册CodeProject来下载该解析器,则可以在Nuget画廊中找到它的LumenWorks.Framework.IO。

– Greg McCoy
2015年9月3日在19:14

#7 楼

这是我投票最多的答案的变体:

var contents = File.ReadAllText(filename).Split('\n');
var csv = from line in contents
          select line.Split(',').ToArray();


然后可以按以下示例使用csv变量: br />

评论


如何访问csv变量中的行和列?

–马修·洛克(Matthew Lock)
15年4月13日在7:02

您如何处理转义逗号?

–张匡威
16年6月11日在2:33

不处理列内的逗号。根据joshb的回答,最好使用健壮的库CsvHelper

– Tim Partridge
19-09-26在16:40



#8 楼

如果需要跳过(标题)行和/或列,则可以使用它来创建二维数组:

    var lines = File.ReadAllLines(path).Select(a => a.Split(';'));
    var csv = (from line in lines               
               select (from col in line
               select col).Skip(1).ToArray() // skip the first column
              ).Skip(2).ToArray(); // skip 2 headlines


需要先对数据进行整形,然后再进行进一步处理(假设前两行由标题组成,第一列是行标题-不需要在数组中,因为您只想查看数据) 。

NB通过使用以下代码,您可以轻松获得标题和第一列: br />

注意:如果需要跳过空行-有时很方便,可以插入

    var coltitle = (from line in lines 
                    select line.Skip(1).ToArray() // skip 1st column
                   ).Skip(1).Take(1).FirstOrDefault().ToArray(); // take the 2nd row
    var rowtitle = (from line in lines select line[0] // take 1st column
                   ).Skip(2).ToArray(); // skip 2 headlines


在上面的LINQ代码示例中的*.csvfrom语句之间。

#9 楼

您可以在C#中使用Microsoft.VisualBasic.FileIO.TextFieldParser dll以获得更好的性能

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

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


评论


它效率不高,因为Split无法完成TextFieldParser的所有工作。例如,跳过注释行,处理带引号的字段,并删除开始/结尾的空格。不完全是1:1的比较。

–罗伯特·麦基(Robert McKee)
15年2月17日在17:37

#10 楼

大家好,我为此创建了一个静态类。
+列检查
+删除定额符


#11 楼

var firstColumn = new List<string>();
var lastColumn = new List<string>();

// your code for reading CSV file

foreach(var line in file)
{
    var array = line.Split(';');
    firstColumn.Add(array[0]);
    lastColumn.Add(array[1]);
}

var firstArray = firstColumn.ToArray();
var lastArray = lastColumn.ToArray();


评论


谢谢你的帮助。这可能有助于解决我的问题。实际上,我必须从文件读取数据,然后将其插入数据库。在插入时,我遇到主键约束错误(因为我已经在数据库中有数据)。因此,我需要进行编程,使变量已经存在,然后更新数据。

–拉沙卜·沙(Rushabh Shah)
11 Mar 12 '11 at 14:25

我假设第一个值是PK-您需要按ID从数据库中获取一条记录,如果存在,则要发出UPDATE语句,否则插入一条新记录。

–雅各布·科内基(Jakub Konecki)
2011-3-12在16:15

#12 楼

我花了几个小时寻找合适的库,但最后我写了我自己的代码:)
您可以使用所需的任何工具读取文件(或数据库),然后将以下例程应用于每一行: >
private static string[] SmartSplit(string line, char separator = ',')
{
    var inQuotes = false;
    var token = "";
    var lines = new List<string>();
    for (var i = 0; i < line.Length; i++) {
        var ch = line[i];
        if (inQuotes) // process string in quotes, 
        {
            if (ch == '"') {
                if (i<line.Length-1 && line[i + 1] == '"') {
                    i++;
                    token += '"';
                }
                else inQuotes = false;
            } else token += ch;
        } else {
            if (ch == '"') inQuotes = true;
            else if (ch == separator) {
                lines.Add(token);
                token = "";
                } else token += ch;
            }
    }
    lines.Add(token);
    return lines.ToArray();
}


#13 楼

这是一种特殊情况,其中一个数据字段包含分号(“;”)作为其数据的一部分,在这种情况下,上面的大多数答案都会失败。 br />
string[] csvRows = System.IO.File.ReadAllLines(FullyQaulifiedFileName);
string[] fields = null;
List<string> lstFields;
string field;
bool quoteStarted = false;
foreach (string csvRow in csvRows)
{
    lstFields = new List<string>();
    field = "";
    for (int i = 0; i < csvRow.Length; i++)
    {
        string tmp = csvRow.ElementAt(i).ToString();
        if(String.Compare(tmp,"\"")==0)
        {
            quoteStarted = !quoteStarted;
        }
        if (String.Compare(tmp, ";") == 0 && !quoteStarted)
        {
            lstFields.Add(field);
            field = "";
        }
        else if (String.Compare(tmp, "\"") != 0)
        {
            field += tmp;
        }
    }
    if(!string.IsNullOrEmpty(field))
    {
        lstFields.Add(field);
        field = "";
    }
// This will hold values for each column for current row under processing
    fields = lstFields.ToArray(); 
}


#14 楼

开源的Angara.Table库允许将CSV加载到类型化的列中,因此您可以从列中获取数组。每列都可以按名称或索引建立索引。参见http://predictionmachines.github.io/Angara.Table/saveload.html。

该库遵循RFC4180的CSV;它启用类型推断和多行字符串。

示例:

using System.Collections.Immutable;
using Angara.Data;
using Angara.Data.DelimitedFile;

...

ReadSettings settings = new ReadSettings(Delimiter.Semicolon, false, true, null, null);
Table table = Table.Load("data.csv", settings);
ImmutableArray<double> a = table["double-column-name"].Rows.AsReal;

for(int i = 0; i < a.Length; i++)
{
    Console.WriteLine("{0}: {1}", i, a[i]);
}


您可以使用列类型查看列类型,例如

Column c = table["double-column-name"];
Console.WriteLine("Column {0} is double: {1}", c.Name, c.Rows.IsRealColumn);


由于该库专注于F#,因此您可能需要添加对FSharp.Core 4.4程序集的引用。单击项目上的“添加引用”,然后在“组件”->“扩展”下选择FSharp.Core 4.4。

#15 楼

我已经使用csvreader.com(付费组件)多年了,我从来没有遇到过问题。它坚固,小巧且快速,但您必须为此付出代价。您可以将定界符设置为任意值。

using (CsvReader reader = new CsvReader(s) {
    reader.Settings.Delimiter = ';';
    reader.ReadHeaders();  // if headers on a line by themselves.  Makes reader.Headers[] available
    while (reader.ReadRecord())
        ... use reader.Values[col_i] ...
}


#16 楼

我只是研究硕士论文的学生,但是这就是我解决问题的方法,对我来说很好。首先,从目录中选择文件(仅采用csv格式),然后将数据放入列表中。

List<float> t = new List<float>();
List<float> SensorI = new List<float>();
List<float> SensorII = new List<float>();
List<float> SensorIII = new List<float>();
using (OpenFileDialog dialog = new OpenFileDialog())
{
    try
    {
        dialog.Filter = "csv files (*.csv)|*.csv";
        dialog.Multiselect = false;
        dialog.InitialDirectory = ".";
        dialog.Title = "Select file (only in csv format)";
        if (dialog.ShowDialog() == DialogResult.OK)
        {
            var fs = File.ReadAllLines(dialog.FileName).Select(a => a.Split(';'));
            int counter = 0;
            foreach (var line in fs)
            {
                counter++;
                if (counter > 2)    // Skip first two headder lines
                {
                    this.t.Add(float.Parse(line[0]));
                    this.SensorI.Add(float.Parse(line[1]));
                    this.SensorII.Add(float.Parse(line[2]));
                    this.SensorIII.Add(float.Parse(line[3]));
                }
            }
        }
    }
    catch (Exception exc)
    {
        MessageBox.Show(
            "Error while opening the file.\n" + exc.Message, 
            this.Text, 
            MessageBoxButtons.OK, 
            MessageBoxIcon.Error
        );
    }
}


#17 楼

还是错的。您需要在引号中加上“”。
这是我的解决方案,微软风格的csv。

评论


当列值内有换行符时,它将无法处理;)

–艾米尔
16 Sep 15'在12:05

#18 楼

我有一个完全可以满足您需求的库。您可以通过以下链接找到它:https://github.com/ukushu/DataExporter

它可以像2维数组一样与CSV一起使用。

例如,如果需要所有第三行的值,则只需要编写以下内容:

Csv csv = new Csv();

csv.FileOpen("c:\file1.csv");

var allValuesOf3rdRow = csv.Rows[2];


或读取

的第二个单元格
var value = csv.Rows[2][1];


#19 楼

使用CsvFramework来查看此内容;使用System.Collections.Generic来查看此内容;使用命名空间CvsParser
{


/>
public class Customer
{
    public int Id { get; set; }
    public string Name { get; set; }
    public List<Order> Orders { get; set; }        
}

public class Order
{
    public int Id { get; set; }

    public int CustomerId { get; set; }
    public int Quantity { get; set; }

    public int Amount { get; set; }

    public List<OrderItem> OrderItems { get; set; }

}

public class Address
{
    public int Id { get; set; }
    public int CustomerId { get; set; }

    public string Name { get; set; }
}

public class OrderItem
{
    public int Id { get; set; }
    public int OrderId { get; set; }

    public string ProductName { get; set; }
}

class Program
{
    static void Main(string[] args)
    {

        var customerLines = System.IO.File.ReadAllLines(@"Customers.csv");
        var orderLines = System.IO.File.ReadAllLines(@"Orders.csv");
        var orderItemLines = System.IO.File.ReadAllLines(@"OrderItemLines.csv");

        CsvFactory.Register<Customer>(builder =>
        {
            builder.Add(a => a.Id).Type(typeof(int)).Index(0).IsKey(true);
            builder.Add(a => a.Name).Type(typeof(string)).Index(1);
            builder.AddNavigation(n => n.Orders).RelationKey<Order, int>(k => k.CustomerId);

        }, false, ',', customerLines);

        CsvFactory.Register<Order>(builder =>
        {
            builder.Add(a => a.Id).Type(typeof(int)).Index(0).IsKey(true);
            builder.Add(a => a.CustomerId).Type(typeof(int)).Index(1);
            builder.Add(a => a.Quantity).Type(typeof(int)).Index(2);
            builder.Add(a => a.Amount).Type(typeof(int)).Index(3);
            builder.AddNavigation(n => n.OrderItems).RelationKey<OrderItem, int>(k => k.OrderId);

        }, true, ',', orderLines);


        CsvFactory.Register<OrderItem>(builder =>
        {
            builder.Add(a => a.Id).Type(typeof(int)).Index(0).IsKey(true);
            builder.Add(a => a.OrderId).Type(typeof(int)).Index(1);
            builder.Add(a => a.ProductName).Type(typeof(string)).Index(2);


        }, false, ',', orderItemLines);



        var customers = CsvFactory.Parse<Customer>();


    }
}


}