29

So I've been reading that I shouldn't write my own CSV reader/writer, so I've been trying to use the CsvHelper library installed via nuget. The CSV file is a grey scale image, with the number of rows being the image height and the number columns the width. I would like to read the values row-wise into a single List<string> or List<byte>.

The code I have so far is:

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

But allValues.ToList<string>() is throwing a:

CsvConfigurationException was unhandled by user code

An exception of type 'CsvHelper.Configuration.CsvConfigurationException' occurred in CsvHelper.dll but was not handled in user code

Additional information: Types that inherit IEnumerable cannot be auto mapped. Did you accidentally call GetRecord or WriteRecord which acts on a single record instead of calling GetRecords or WriteRecords which acts on a list of records?

GetRecords is probably expecting my own custom class, but I'm just wanting the values as some primitive type or string. Also, I suspect the entire row is being converted to a single string, instead of each value being a separate string.

Ayb4btu
  • 3,208
  • 5
  • 30
  • 42
  • Can you give us more information of the CsvConfiguration exception? Is there a message? – masinger Oct 23 '15 at 04:14
  • 1
    Calling .ToLIst() like this is almost always a mistake. Stay with IEnumerable for as long as possible for best performance. – Joel Coehoorn Oct 23 '15 at 04:44
  • Probably would've been helpful to show your CSV file. At any rate, another option is [SoftCircuits.CsvParser](https://www.nuget.org/packages/SoftCircuits.CsvParser/). Not sure if it would help here but I found it to average about four times faster than CsvHelper. – Jonathan Wood Feb 15 '20 at 02:01

6 Answers6

30

According to @Marc L's post you can try this:

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;
}
masinger
  • 775
  • 6
  • 13
  • This gives the answer I was initially seeking, so I'll accept this as the answer, but I think @MarcL. is correct in that CsvHelper doesn't appear to be designed for the CSV files I have. – Ayb4btu Oct 23 '15 at 07:35
  • 1
    You know you can use the VB csv library to parse a csv file, right? you can reference it and use it just as if it was a c# library. –  Oct 23 '15 at 16:07
  • 3
    Should ```var csv = new CsvReader(fileReader);``` also be in a using() statement as CsvReader is IDisposable? (may of not been the time of the answer) – Alex KeySmith Jun 13 '18 at 21:56
18

If all you need is the string values for each row in an array, you could use the parser directly.

var parser = new CsvParser( textReader );
while( true )
{
    string[] row = parser.Read();
    if( row == null )
    {
        break;
    }
}

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

Update

Version 3 has support for reading and writing IEnumerable properties.

d219
  • 2,707
  • 5
  • 31
  • 36
Josh Close
  • 22,935
  • 13
  • 92
  • 140
  • 2
    Just curious: does this really give you anything that you don't already get with `File.ReadAllLines()`, that justifies the cost of adding another library dependency? Also, isn't `CsvParser` an `IDisposable`? – Marc L. Oct 23 '15 at 21:19
  • 9
    It splits up the line into fields based on RFC 4180, has some configuration to work with non-standard files, and fall back to handling it like Excel does. What if your field has a `\n` in it? `ReadAllLines()` would give you 2 lines, when it's just 1 CSV row. – Josh Close Oct 23 '15 at 21:27
  • I am using csvhelper created by you and i want to read all csv data in to data reader.is it possible as i cant find any example which states how to convert data in data reader – I Love Stackoverflow Dec 28 '16 at 07:30
  • What is textReader? – Neberu Jun 08 '18 at 21:53
  • `File.ReadAllLines()` will not work when we have `\n` in the cell value. I am having that issue. – JackFrost Aug 01 '22 at 12:05
  • @Neberu System.IO.TextReader - just in case you haven't found it yet. :) – The incredible Jan Jul 05 '23 at 14:31
  • @JoshClose The link doesn't work right (anymore?) - is your project still alive? The documentation doesn't really look good... – The incredible Jan Jul 05 '23 at 14:49
  • parser.Read() returns boolean in version 27 of CsvHelper ... :/ – The incredible Jan Jul 05 '23 at 15:01
5

The whole point here is to read all lines of CSV and deserialize it to a collection of objects. I'm not sure why do you want to read it as a collection of strings. Generic ReadAll() would probably work the best for you in that case as stated before. This library shines when you use it for that purpose:

using System.Linq;

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

If you don't use ToList() - it will return a single record at a time (for better performance), please read https://joshclose.github.io/CsvHelper/examples/reading/enumerate-class-records

d219
  • 2,707
  • 5
  • 31
  • 36
lead
  • 91
  • 1
  • 6
3

Please try this. This had worked for me.

TextReader reader = File.OpenText(filePath);
            CsvReader csvFile = new CsvReader(reader);
            csvFile.Configuration.HasHeaderRecord = true;
            csvFile.Read();
            var records = csvFile.GetRecords<Server>().ToList();

Server is an entity class. This is how I created.

 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;
            }
        }       
    }
sal
  • 49
  • 2
1

You are close. It isn't that it's trying to convert the row to a string. CsvHelper tries to map each field in the row to the properties on the type you give it, using names given in a header row. Further, it doesn't understand how to do this with IEnumerable types (which string implements) so it just throws when it's auto-mapping gets to that point in testing the type.


That is a whole lot of complication for what you're doing. If your file format is sufficiently simple, which yours appear to be--well known field format, neither escaped nor quoted delimiters--I see no reason why you need to take on the overhead of importing a library. You should be able to enumerate the values as needed with System.IO.File.ReadLines() and 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;
  }
}
Marc L.
  • 3,296
  • 1
  • 32
  • 42
  • So CsvHelper is designed for handling CSV files with specific columns and passing them into a custom class that contains parameters for those columns? Is it possible to use CsvHelper in my situation, or does that require iterating over each field? – Ayb4btu Oct 23 '15 at 04:44
  • Honestly I haven't worked with it before, but reading over the docs and the error you received implies that it was not built for your task. You could maybe twist it to get what you need, but I think at that point you are far, far better off just using the CLR tools available to you. – Marc L. Oct 23 '15 at 05:14
  • 4
    I don't think this is a correct way. csv files can have quoted text that has commas in them, and you cannot split on any comma – Emad Jun 26 '19 at 04:48
  • @Emad "well known field format, neither escaped nor quoted delimiters" – The incredible Jan Jul 06 '23 at 06:23
-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();

    }
d219
  • 2,707
  • 5
  • 31
  • 36
mognito
  • 1
  • 2
  • 2
    This is not an answer to 'Read all values from CSV into a List using CsvHelper' - this answer concerns writing files. – Wai Ha Lee May 23 '20 at 09:46