0

I want to put a data from simple csv file into the records containing custom made class. Here is my code:

using System;
using CsvHelper;
using System.IO;    // for accessing the files
using System.Globalization;
using System.Linq;  // to call a list enumerable
using CsvHelper.Configuration;
using CsvHelper.Configuration.Attributes;

namespace Reading_CSV_Files
{
    class Program
    {
        static void Main(string[] args)
        {
            ReadCSVFile(@"C:\path_to_my_file\file.csv");
            
        }



    public static void ReadCSVFile(String filePath)
        {
            if (filePath == null)
            {
                return;
            }

            using (var streamReader = new StreamReader(filePath) )
            {
                using (var foodFileCSVReader = new CsvReader(streamReader, 
                    CultureInfo.InvariantCulture))
                {
                    //var records = foodFileCSVReader.GetRecords<dynamic>().ToList();
                    var records = foodFileCSVReader.GetRecords<Pizza>().ToList();
                    // replace dynamic type argument on our records

                }
            }
        }
    }

    public class Pizza 
    {
        // attributes
        [Name("Name")]
        public String Name { get; set; }

        [Name("PLN_Cost")]
        public double Price { get; set; }
    }
}

The csv file looks like this: Screenshot from csv file The file was saved as comma separated. I found some advices with manual setting it up, but currently it says, this field is read-only.

CsvHelper.HeaderValidationException: Header with name 'Name'[0] was not found. Header with name 'PLN_Cost'[0] was not found.

  • If you open the CSV file in a hex editor, does it start with the (hex) bytes 4E 61 6D ? – Andrew Morton Apr 29 '21 at 17:36
  • I don't understand what do you mean by hex editor. I'm using windows 10. – Beginner_in_R Apr 29 '21 at 17:38
  • Please see [Can I hex edit a file in Visual Studio?](https://stackoverflow.com/a/1724591/1115360) for instructions. – Andrew Morton Apr 29 '21 at 17:41
  • Yes. It starts like this: 4E 61 6D 65 3B 50 4C – Beginner_in_R Apr 29 '21 at 17:42
  • Aha! That's "Name;PL", so you need to tell the CSV reader to use semicolons as the separator instead of commas. You didn't give a link to the exact CSV helper you're using, so I can't tell you the exact instruction, but you should be able to find it in the documentation. – Andrew Morton Apr 29 '21 at 17:45
  • I found that in previous versions it could be set up, but currently it says that it's read-only.I'm using CSVHeelper 27.0.2 by Josh Close on visual studio 2019. .Net in version 4.8 – Beginner_in_R Apr 29 '21 at 17:48
  • @Beginner_in_R Does [How we can write delimiter like sep=, using CsvHelper library?](https://stackoverflow.com/a/66532784/1115360) help? – Andrew Morton Apr 29 '21 at 17:52
  • It shows red foodFileCSVReader.WriteField("sep=,", false); and says that does not contain definition for WriteField – Beginner_in_R Apr 29 '21 at 17:56
  • @Andrew Morton I don't know if that helps, but I copied and pasted all content from this file into the google sheet and downloaded it. Now it works fine. Maybe its something related to settings on my OS (polish). – Beginner_in_R Apr 29 '21 at 18:03
  • The usual CSV separator in mainland Europe is the semi-colon because a comma is used as the decimal separator. I guess that Google Sheet uses commas in CSV files and full stops `.` for the decimal separator. – Andrew Morton Apr 29 '21 at 18:48

1 Answers1

0

If the program is going to be using CSV files which might have a comma or semi-colon as the separator, you could read the first line and set the separator to either of those, like this:

using CsvHelper;
using CsvHelper.Configuration;
using CsvHelper.Configuration.Attributes;
using System;
using System.Collections.Generic;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Text;

namespace ConsoleApp1
{
    class Program
    {

        public class Pizza
        {
            // attributes
            [Name("Name")]
            public String Name { get; set; }

            [Name("PLN_Cost")]
            public decimal Price { get; set; }

            public override string ToString()
            {
                return $"{Name} - {Price}";
            }
        }

        public static List<Pizza> ReadCSVFile(String filePath)
        {
            if (!File.Exists(filePath))
            {
                return new List<Pizza>();
            }

            var sep = ";";

            /* Check the header for the separator character to use. */
            string headerLine = File.ReadLines(filePath).First();
            if (headerLine?.IndexOf(',') >= 0) { sep = ","; }

            using (var sr = new StreamReader(filePath))
            {
                var config = new CsvConfiguration(CultureInfo.CurrentCulture)
                {
                    Delimiter = sep,
                    Encoding = Encoding.UTF8
                };

                using (var foodFileCSVReader = new CsvReader(sr, config))
                {
                    return foodFileCSVReader.GetRecords<Pizza>().ToList();

                }
            }
        }

        static void Main(string[] args)
        {
            var pp = ReadCSVFile(@"C:\temp\PizzaPrices.csv");
            Console.WriteLine(string.Join("\r\n", pp));
            Console.ReadLine();
        }

    }

}

Note that it is better to use the decimal type for money instead of the double type.

You might need additional code to set the decimal separator to be used.

Andrew Morton
  • 24,203
  • 9
  • 60
  • 84