使用CsvHelper将所有CSV值读入List

29

我一直在阅读建议不要自己编写CSV读取器/写入器的内容,于是我尝试使用通过nuget安装的CsvHelper库。CSV文件是灰度图像,行数为图像高度,列数为宽度。我想将值按行读入单个List<string>List<byte>中。

到目前为止,我的代码如下:

using CsvHelper;

public static List<string> ReadInCSV(string absolutePath)
{
    IEnumerable<string> allValues;

    using (TextReader fileReader = File.OpenText(absolutePath))
    {
        var csv = new CsvReader(fileReader);
        csv.Configuration.HasHeaderRecord = false;
        allValues = csv.GetRecords<string>
    }

    return allValues.ToList<string>();
}

但是allValues.ToList<string>()引发了一个:

CsvConfigurationException未被用户代码处理

类型为'CsvHelper.Configuration.CsvConfigurationException'的异常在CsvHelper.dll中发生,但未在用户代码中处理

额外信息:继承IEnumerable的类型无法自动映射。 您是否意外调用了GetRecord或WriteRecord(作用于单个记录)而不是调用GetRecords或WriteRecords(作用于记录列表)?

GetRecords可能期望我的自定义类,但我只想要一些基本类型或字符串值。 同样,我怀疑整个行被转换为单个字符串,而不是每个值都是单独的字符串。


你能否提供有关CsvConfiguration异常的更多信息?有没有错误消息? - masinger
1
像这样调用 .ToList() 几乎总是一个错误。尽可能长时间地保持 IEnumerable,以获得最佳性能。 - Joel Coehoorn
可能展示一下你的CSV文件会更有帮助。无论如何,另一个选择是SoftCircuits.CsvParser。不确定它是否对这里有所帮助,但我发现它的速度大约比CsvHelper快四倍。 - Jonathan Wood
7个回答

30
根据@Marc L的帖子,您可以尝试这个:
public static List<string> ReadInCSV(string absolutePath) {
    List<string> result = new List<string>();
    string value;
    using (TextReader fileReader = File.OpenText(absolutePath)) {
        var csv = new CsvReader(fileReader);
        csv.Configuration.HasHeaderRecord = false;
        while (csv.Read()) {
           for(int i=0; csv.TryGetField<string>(i, out value); i++) {
                result.Add(value);
            }
        }
    }
    return result;
}

这个回答给了我最初寻找的答案,所以我会接受这个答案,但我认为 @MarcL. 是正确的,因为 CsvHelper 似乎不适用于我所拥有的 CSV 文件。 - Ayb4btu
1
你知道你可以使用VB csv库来解析csv文件,对吧?你可以引用它并像使用c#库一样使用它。 - user4843530
3
var csv = new CsvReader(fileReader); 这行代码是否也应该放在 using() 语句中,因为 CsvReader 是 IDisposable 接口?(回答时可能没有考虑到这一点) - Alex KeySmith

18
如果你只需要一个数组中每行的字符串值,你可以直接使用解析器。
var parser = new CsvParser( textReader );
while( true )
{
    string[] row = parser.Read();
    if( row == null )
    {
        break;
    }
}

http://joshclose.github.io/CsvHelper/#reading-parsing

更新

第三个版本支持读写IEnumerable属性。


2
只是好奇:这个东西真的能给你任何 File.ReadAllLines() 不能提供的东西吗?这是否值得增加另一个库依赖的成本?此外,CsvParser 是一个 IDisposable 吗? - Marc L.
9
根据RFC 4180标准,它将该行拆分为字段,并具有一些配置以处理非标准文件,并回退到像Excel一样处理的方式。如果您的字段中有\n怎么办? ReadAllLines()会将其视为两行,而不是一个CSV行。 - Josh Close
我正在使用由您创建的CsvHelper,并且我想将所有csv数据读入到数据阅读器中。是否有可能实现?因为我找不到任何示例说明如何将数据转换为数据阅读器。 - Learning-Overthinker-Confused
什么是textReader? - Neberu
当单元格值中存在\n时,File.ReadAllLines()将无法正常工作。我遇到了这个问题。 - JackFrost
显示剩余3条评论

5
这里的重点是读取CSV的所有行并将其反序列化为对象集合。我不确定为什么您想将其作为字符串集合读取。在这种情况下,通用的`ReadAll()`可能是最好的选择,如前所述。只有当您将其用于此目的时,该库才会发挥其优势。
using System.Linq;

...
using (var reader = new StreamReader(path))
using (var csv = new CsvReader(reader))
{
    var yourList = csv.GetRecords<YourClass>().ToList();
}

如果您不使用 ToList() - 它将一次返回一条记录(以提高性能),请阅读https://joshclose.github.io/CsvHelper/examples/reading/enumerate-class-records

CsvReader不接受reader作为参数。我们需要使用特定的版本吗? - JackFrost
@JackFrost 当然可以。看这个链接:https://joshclose.github.io/CsvHelper/getting-started/ - The incredible Jan
@lead 你好像没有理解问题。他没有一个对象,而是有一个列数可能会变化的列表。 - The incredible Jan

3
请尝试这个。这对我有用过。
TextReader reader = File.OpenText(filePath);
            CsvReader csvFile = new CsvReader(reader);
            csvFile.Configuration.HasHeaderRecord = true;
            csvFile.Read();
            var records = csvFile.GetRecords<Server>().ToList();

Server是一个实体类。以下是我创建该类的方式。

 public class Server
    {
        private string details_Table0_ProductName;
        public string Details_Table0_ProductName
        {
            get
            {
                return details_Table0_ProductName;
            }
            set
            {
                this.details_Table0_ProductName = value;
            }
        }

        private string details_Table0_Version;
        public string Details_Table0_Version
        {
            get
            {
                return details_Table0_Version;
            }
            set
            {
                this.details_Table0_Version = value;
            }
        }       
    }

1
CsvReaderIDisposable 接口,我认为我们需要使用 using 语句。 - JackFrost
1
他没有实体类。它有一个包含可变列数的列表/表格。 - The incredible Jan

1

你很接近了。事实上,它并不是在试图将转换为字符串。CsvHelper尝试将行中的每个字段映射到给定类型的属性上,使用头行中给出的名称。此外,它不理解如何处理IEnumerable类型(string实现),因此当它在测试类型时到达该点时,它会抛出异常。


对于你所做的事情来说,这是非常复杂的。如果你的文件格式足够简单,而且你的字段格式是众所周知的,既没有转义也没有引用分隔符,那么我认为你没有必要承担导入库的开销。你应该能够使用 System.IO.File.ReadLines()String.Split() 枚举所需的值。

//pseudo-code...you don't need CsvHelper for this
IEnumerable<string> GetFields(string filepath)
{
  foreach(string row in File.ReadLines(filepath))
  {
    foreach(string field in row.Split(',')) yield return field;
  }
}

CsvHelper是专门设计用于处理具有特定列的CSV文件,并将它们传递到包含这些列参数的自定义类中的吗?在我的情况下,是否可以使用CsvHelper,还是需要迭代每个字段? - Ayb4btu
说实话,我以前没有使用过它,但是阅读文档和您收到的错误表明它不适用于您的任务。您可以尝试扭曲它以获得所需内容,但我认为在那一点上,您最好使用可用的CLR工具。 - Marc L.
4
我不认为这是正确的方法。CSV文件中的文本可能包含逗号,如果你只是根据逗号进行分割就会有问题。 - Emad
@Emad "众所周知的字段格式,既不转义也不引用分隔符" - The incredible Jan

0
以下.NET代码可以将CSV文件中的所有单元格值转换为List<string>对象。已经测试过,可以成功读取包含11列X 31行的CSV文件中的所有单元格数据。 首先,安装CsvHelper 30.0.1,然后在您的代码中使用该库。
using CsvHelper;
using CsvHelper.Configuration;

然后,使用这些代码将单元格的值保存到.NET列表字符串对象中。
    // Replace with your CSV file path
    string filePath = @"C:\Temp\MyRawData.csv";

    // Configure CSV Reader
    CsvConfiguration config = new CsvConfiguration(CultureInfo.InvariantCulture)
    { HasHeaderRecord = false };
    StreamReader reader = new StreamReader(filePath);

    // Declare List<string> Object to hold data later
    List<string> cellDataList = new List<string>();

    // Fetch all cells value
    using (var csv = new CsvReader(reader, config))
    {

        while (csv.Read())
        {
            int MaxIndex = csv.Parser.Count;
            for (var i = 0; i < MaxIndex; i++)
            {
                // Then Save into List<string> Object
                cellDataList.Add(csv.GetField<string>(i));
            }
        }
    }

-4
static void WriteCsvFile(string filename, IEnumerable<Person> people)
    {

        StreamWriter textWriter = File.CreateText(filename);

        var csvWriter = new CsvWriter(textWriter, System.Globalization.CultureInfo.CurrentCulture);

        csvWriter.WriteRecords(people);

        textWriter.Close();

    }

2
这不是“使用CsvHelper从CSV中读取所有值到列表”的答案 - 这个答案涉及文件写入。 - Wai Ha Lee

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接