I want to read a csv file that has 9 columns with headers and many data rows below, but I am just interested in three of the columns, and they are not contiguous. I have tried with this code but it doesn't work, it stops in the foreach loop with a runtime exception from CsvHelper "'Field with name 'Y' does not exist. You can ignore missing fields by setting MissingFieldFound to null.'".
The csv file is like this:
FrameNO , Ttotal, TNo, X, Y , Z , Speed , Intensity, ILog ;
1 , 9 , 1 , 0.08, 1.4 , 0 , 0 , 78 , 19 , ;;
1 , 9 , 2 ,0.1 , 1.56 , 0 , 0 , 228 , 28, ;;
using CsvHelper;
namespace RadarPrototipo.Clases
{
public class Foo
{
public int FrameNO { get; set; }
public double Y { get; set; }
public int Intensity { get; set; }
}
class CCalc
{
public double Calc(int f)
{
double d=1.5;
int inten=0;
using (var reader = new StreamReader("C:/Users/Usuario/Desktop/Uni/AlumnoInterno/grab.csv"))
using (var csv = new CsvReader(reader))
{
csv.Configuration.Delimiter = ",";
var records = csv.GetRecords<Foo>();
csv.Configuration.HeaderValidated = null;
foreach (var Foo in records)
{
if (Foo.FrameNO == f)
{
if (Foo.Y < 1.8 && Foo.Y > 1.5)
{
if (Foo.Intensity > inten)
{
inten = Foo.Intensity;
d = Foo.Y;
}
}
}
}
}
return d;
}
}
}
The function analyses the data on those three columns and selects the best answer according to the conditions, then returns the value Y which is a distance.
Any help is really thanked.
The following works for me.
public class Program
{
public static void Main(string[] args)
{
using (MemoryStream stream = new MemoryStream())
using (StreamWriter writer = new StreamWriter(stream))
using (StreamReader reader = new StreamReader(stream))
using (CsvReader csv = new CsvReader(reader))
{
writer.WriteLine("FrameNO,Column2,Y,Column4,Column5,Column6,Intensity,Column8,Column9");
writer.WriteLine("1,two,1.123,four,five,six,10,eight,nine");
writer.WriteLine("2,two,2.345,four,five,six,20,eight,nine");
writer.Flush();
stream.Position = 0;
var records = csv.GetRecords<Foo>();
foreach (var Foo in records)
{
Console.WriteLine(Foo.FrameNO);
}
}
Console.ReadLine();
}
}
public class Foo
{
public int FrameNO { get; set; }
public double Y { get; set; }
public int Intensity { get; set; }
}
One thing you might try is setting your delimiter. Your cultural default might not be a comma. Also, if you have spaces between your data and the commas, you will need to set the TrimOptions.
using (var csv = new CsvReader(reader))
{
csv.Configuration.Delimiter = ",";
csv.Configuration.TrimOptions = TrimOptions.Trim;
var records = csv.GetRecords<Foo>();
Related
So i am having a problem importing a csv file, i want to make an object from the columns but i cant read in properly.
So the header line looks like this: Title,Year,Genre,Rating,Votes,Directors
The data line looks like this: The Last of Us: Mass Effect 2,2010,"Action, Adventure, Drama",9.5,19961,Casey Hudson
The problem is that, i get the exception "Input string is not in correct form"
I am using coma as delimiter, is there a way to make quotes as delimiters too?
Also, what are in the qoutes belongs to the Genre attribute.
I am using this code as the CsvParser right now:
using Games.Models;
using System.Globalization;
using System.Text;
namespace Games.Utils
{
public class CsvParser
{
private readonly string _path;
public char Delimiter { get; set; } = ',';
public bool SkipFirst { get; set; } = true;
public bool Verbose { get; set; } = true;
public NumberFormatInfo NumberFormatInfo { get; private set; } = new NumberFormatInfo();
public Encoding Encoding { get; set; } = Encoding.Default;
public CsvParser(string path) => _path = path;
public IEnumerable<Game> StreamParseGames() => GenerateGames(Enumerables.EnumerateStreamReaderLines(new(_path, Encoding)));
public IEnumerable<Game> TextParseGames() => GenerateGames(File.ReadAllLines(_path, Encoding));
private IEnumerable<Game> GenerateGames(IEnumerable<string> lineProvider)
{
if (SkipFirst) lineProvider = lineProvider.Skip(1);
int lineNum = SkipFirst ? 1 : 0;
foreach (var line in lineProvider)
{
string[] parts = line.Split(Delimiter);
Game game;
try
{
game = new()
{
Title = parts[0],
Year = Convert.ToInt32(parts[1], NumberFormatInfo),
Genre = parts[2],
Rating = Convert.ToDouble(parts[3], NumberFormatInfo),
Votes = Convert.ToDouble(parts[4], NumberFormatInfo),
Directors = parts[5],
};
}
catch (FormatException e)
{
if (Verbose) Console.WriteLine($"Line {lineNum + 1:000000} omitted due: {e.Message}");
continue;
}
catch (IndexOutOfRangeException e)
{
if (Verbose) Console.WriteLine($"Line {lineNum + 1:000000} omitted due: {e.Message}");
continue;
}
finally
{
++lineNum;
}
yield return game;
}
}
}
}
I'd suggest you use CsvHelper which can deal with that instead of rolling your own CSV parser.
using CsvHelper;
using CsvHelper.Configuration;
var config = new CsvConfiguration(CultureInfo.InvariantCulture)
{
Delimiter = ",",
};
using (var reader = new StreamReader("path\\to\\file.csv"))
using (var csv = new CsvReader(reader, config))
{
var records = csv.GetRecords<Foo>();
}
I have a CSV File with multiple fields(check below for format)
ArticleNumber;Shop1;Shop2;Shop3;Shop4;Shop5;Shop6;Shop7
123455;50;51;52;53;54;55;56
In fields Shop1,Shop2....Shop7 I have product Prices. I receive file like this, so I need to find a cool way to solve my problem.
I want to read this CSV using CsvHelper library, but I don't know how to map fields. As a result I want something like this:
ArticleNumber
Shop
Price
123455
Shop1
50
123455
Shop2
51
123455
Shop3
52
123455
Shop4
53
123455
Shop5
54
123455
Shop6
55
I think this will get you the format you are looking for.
void Main()
{
var config = new CsvConfiguration(CultureInfo.InvariantCulture)
{
Delimiter = ";"
};
using (var reader = new StringReader("ArticleNumber;Shop1;Shop2;Shop3;Shop4;Shop5;Shop6;Shop7\n123455;50;51;52;53;54;55;56\n123456;60;61;62;63;64;65;66"))
using (var csv = new CsvReader(reader, config))
{
csv.Context.RegisterClassMap<ArticleMap>();
csv.Read();
csv.ReadHeader();
var shops = csv.HeaderRecord.Skip(1).ToArray();
var records = csv.GetRecords<Article>().ToList();
var results = records.SelectMany(r => r.Shop
.Select((s, i) => new ArticleShop
{
ArticleNumber = r.ArticleNumber,
Shop = shops[i],
Price = s
})
).ToList();
}
}
public class ArticleMap : ClassMap<Article>
{
public ArticleMap()
{
Map(x => x.ArticleNumber);
Map(x => x.Shop).Index(1);
}
}
public class Article
{
public int ArticleNumber { get; set; }
public List<double> Shop { get; set; }
}
public class ArticleShop
{
public int ArticleNumber { get; set; }
public string Shop { get; set; }
public double Price { get; set; }
}
Is the format of your CSV File set, or can it be changed?
If it can be changed you could change it to
ArticleNumber;Shop;Price
123455;Shop1;50
123455;Shop2;51
and so on
Edit: As I said in my comment you could also do it like this (this is Pseudo code only, I dont have c# open)
class PriceForArticle{
int articleNumber;
string shopName;
float price;
}
and then you would have this Methode to initialise them into a list of PriceForArticle
List<PriceForArticle> prices = new List<PriceForArticle>();
for(int j = 1; j < AllArticles.Length; j++){
for(int i = 1; i < AllArticles[j].Length; i++){
prices.Add(new PriceForArticle(AllArticles[j][0], AllArticles[0][i], AllArticles[j][i]));
}
}
I am using the following code and getting an exception ,
Exception thrown: 'CsvHelper.WriterException' in CsvHelper.dll on this line executed:
csv.WriteRecord(item);
This is the more of the code:
using (var memoryStream = new MemoryStream())
{
using (var writer = new StreamWriter(memoryStream))
{
using (var csv = new CsvHelper.CsvWriter(writer, System.Globalization.CultureInfo.CurrentCulture))
{
foreach (var item in csvData)
{
csv.WriteRecord(item); // Exception thrown here
}
}
}
var arr = memoryStream.ToArray();
//js.SaveAs("people.csv", arr); what type object is js? copied from the stackoverflow answer linked here
}
This is the csvData code:
IEnumerable<DateLowHigh> csvData = stocks.candles
.Select(c => new DateLowHigh
{
Time = c.datetime,
Close = c.close,
Low = c.low,
High = c.high
})
.ToList();
I do get the csvData.
This stackoverflow answer helped me get started.
I don't know why you need to use CsvHelper when you can easily do the same with the IO library.
sing System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;
namespace ConsoleApplication1
{
class Program
{
const string FILENAME = #"c:\temp\test.csv";
static void Main(string[] args)
{
Stocks stocks = new Stocks();
IEnumerable<DateLowHigh> csvData = stocks.candles.Select(c => new DateLowHigh
{
Time = c.datetime,
Close = c.close,
Low = c.low,
High = c.high
}).ToList();
MemoryStream memoryStream = new MemoryStream();
using (var writer = new StreamWriter(memoryStream))
{
string[] headers = {"Time", "Close", "Low", "High"};
writer.WriteLine(string.Join(",", headers));
foreach (var item in csvData)
{
writer.WriteLine(string.Join(",", new string[] { item.Time.ToString(), item.Close, item.Low.ToString(), item.High.ToString() }));
}
}
memoryStream.Position = 0;
}
}
public class Stocks
{
public List<Candle> candles { get; set; }
}
public class Candle
{
public DateTime datetime { get; set; }
public string close { get; set; }
public int low { get; set; }
public int high { get; set; }
}
public class DateLowHigh
{
public DateTime Time { get; set; }
public string Close { get; set; }
public int Low { get; set; }
public int High { get; set; }
}
}
To answer your question as to what type of object js is in js.SaveAs("people.csv", arr); it is likely Microsoft.JSInterop.IJSRuntime as seen in this Blazer HowTo. The StackOverflow question you referenced was likely using it to get access to JavaScript they could use in C# code. If you just want to save a CSV file using CsvHelper, it could be as simple as:
using (var writer = new StreamWriter("path\\to\\file.csv"))
using (var csv = new CsvWriter(writer, CultureInfo.InvariantCulture))
{
csv.WriteRecords(csvData);
}
If you are still getting errors, you would need to post the full exception with StackTrace in order to help further.
I have an implementation of CSV helper which currently reads CSV's in the traditional format:
Name, Address, Age
"Foo", "Foo's address", 24
"Bar", "Bar's address", 19
I use a class map to map the fields by name to my Person object in the following way:
using (var reader = new StreamReader(file, Encoding.UTF8))
{
using (var csvReader = new CsvReader(reader))
{
csvReader.Configuration.RegisterClassMap<ContentMapper>();
var records = csvReader.GetRecords<Person>().ToArray();
}
}
I need an implementation which reads the exact same data but in a vertical format which originates from data where the column names are vertical in the first column and the data follows in columns instead of rows.
Name, "Foo", "Bar"
Address, "Foo's address", "Bar's address"
Age, 24, 19
What would be the best way to handle a CSV in this format whilst retaining the original mapping?
Try this one
public class Person
{
public string Name { get; set; }
public string Address { get; set; }
public int Age { get; set; }
public List<Person> ReadFile(string path)
{
char[] charsToTrim1 = {'\\', ' ', '"', '\"'};
var fileData = File.ReadAllLines(path);
var outputData = new List<Person>();
for (var i = 0; i < fileData.Length; i++)
{
var tmpData = fileData[i].Split(',');
for (var j = 0; j < tmpData.Length; j++)
{
var t1 = tmpData[j].Trim(charsToTrim1);
if (j == 0)
continue;
switch (i)
{
case 0:
{
var tmPerson = new Person {Name = t1};
outputData.Add(tmPerson);
}
break;
case 1:
{
outputData[j - 1].Address = t1;
}
break;
case 2:
{
outputData[j - 1].Age = Convert.ToInt32(t1);
}
break;
}
}
}
return outputData;
}
}
This could probably be cleaned up a bit, but it does seem to work.
Use CsvHelper to pull in the records as List<dynamic>
Rotate the records into a new List<dynamic> so the first field in each row becomes the property name of the dynamic record.
Use CsvHelper to write the new list to memory.
Use CsvHelper to read the records back in using your ClassMap
public class Program
{
public static void Main(string[] args)
{
var flippedRecords = new List<dynamic>();
using (MemoryStream stream = new MemoryStream())
using (StreamWriter writer = new StreamWriter(stream))
using (StreamReader reader = new StreamReader(stream))
using (CsvReader csv = new CsvReader(reader))
{
writer.WriteLine("Name,Foo,Bar");
writer.WriteLine("Address,Foo's address,\"Bar's address with, comma\"");
writer.WriteLine("Age,24,19");
writer.Flush();
stream.Position = 0;
csv.Configuration.HasHeaderRecord = false;
// Get the records from the CSV file.
var records = csv.GetRecords<dynamic>().ToList();
// Rotate the records into a new dynamic list.
var rows = new List<IDictionary<string, object>>();
foreach (var row in records)
{
rows.Add(row as IDictionary<string, object>);
}
for (int i = 2; i <= rows[0].Count; i++)
{
var flippedRecord = new ExpandoObject() as IDictionary<string, object>;
foreach (var row in rows)
{
flippedRecord.Add((string)row["Field1"], row["Field" + i]);
}
flippedRecords.Add(flippedRecord);
}
}
using (MemoryStream stream = new MemoryStream())
using (StreamWriter writer = new StreamWriter(stream))
using (CsvWriter csvWriter = new CsvWriter(writer))
using (StreamReader reader = new StreamReader(stream))
using (CsvReader csvReader = new CsvReader(reader))
{
// Write the new list to memory
csvWriter.WriteRecords(flippedRecords);
writer.Flush();
stream.Position = 0;
// Read in the person records using a ClassMap.
csvReader.Configuration.RegisterClassMap<PersonMap>();
var people = csvReader.GetRecords<Person>().ToArray();
}
}
}
public class Person
{
public string FirstName { get; set; }
public string LastName { get; set; }
public string Address { get; set; }
public int Age { get; set; }
}
public class PersonMap : ClassMap<Person>
{
public PersonMap()
{
Map(m => m.FirstName).Name("Name");
Map(m => m.Address);
Map(m => m.Age);
}
}
Say we want to calculate how the price of some fruits are changing. Starting from a CSV file:
Day,Name,Kind,Price
2019-09-04,"apple","red delicious",63.09
2019-09-04,"apple","ginger crisp",52.14
2019-09-04,"orange","navel",41.18
2019-09-03,"apple","red delicious",63.07
2019-09-03,"apple","ginger crisp",52.11
2019-09-03,"orange","navel",41.13
2019-09-02,"apple","red delicious",63.00
2019-09-02,"apple","ginger crisp",52.00
2019-09-02,"orange","navel",41.00
with an unknown number of fruits and varieties, we can read the dataframe and build an extra column to use for matching.
var fruits_file = Path.Combine(root, "fruits.csv");
Deedle.Frame<int, string> df = Frame.ReadCsv(fruits_file);
Series<int, string> name = df.GetColumn<string>("Name");
Series<int, string> kind = df.GetColumn<string>("Kind");
var namekind = name.ZipInner(kind).Select(t => t.Value.Item1 + t.Value.Item2);
df.AddColumn("NameKind", namekind);
but the problem remains. Deedle.Series.Window() and Deedle.Series.Pairwise() make it possible to perform first-order differences, but not matching based on some string (namekind).
What is the right way to copy over a column LastPrice and subsequently calculate the Change?
Day,Name,Kind,Price,LastPrice,Change
2019-09-04,"apple","red delicious",63.09,63.07,0.02
2019-09-04,"apple","ginger crisp",52.14,52.11,0.03
2019-09-04,"orange","navel",41.18,41.13,0.05
2019-09-03,"apple","red delicious",63.07,63.00,0.07
2019-09-03,"apple","ginger crisp",52.11,52.00,0.11
2019-09-03,"orange","navel",41.13,41.00,0.13
2019-09-02,"apple","red delicious",63.00,,
2019-09-02,"apple","ginger crisp",52.00,,
2019-09-02,"orange","navel",41.00,,
See code below :
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Data;
using System.IO;
namespace ConsoleApplication137
{
class Program
{
const string FILENAME = #"c:\temp\test.csv";
static void Main(string[] args)
{
Fruit fruit = new Fruit(FILENAME);
Fruit.PrintFruits();
Console.ReadLine();
}
}
public class Fruit
{
public static List<Fruit> fruits { get; set; }
public DateTime day { get; set; }
public string name { get; set; }
public string kind { get; set; }
public decimal price { get; set; }
public Fruit() { }
public Fruit(string filename)
{
int count = 0;
StreamReader reader = new StreamReader(filename);
string line = "";
while ((line = reader.ReadLine()) != null)
{
if (++count > 1)
{
string[] splitLine = line.Split(new char[] { ',' }).ToArray();
Fruit newFruit = new Fruit();
if (fruits == null) fruits = new List<Fruit>();
fruits.Add(newFruit);
newFruit.day = DateTime.Parse(splitLine[0]);
newFruit.name = splitLine[1];
newFruit.kind = splitLine[2];
newFruit.price = decimal.Parse(splitLine[3]);
}
}
reader.Close();
}
public static void PrintFruits()
{
var groups = fruits.OrderBy(x => x.day)
.GroupBy(x => new { name = x.name, kind = x.kind })
.ToList();
foreach (var group in groups)
{
for (int i = 0; i < group.Count() - 1; i++)
{
Console.WriteLine("Old Date : '{0}', New Date : '{1}', Name : '{2}', Kind : '{3}', Old Price '{4}', New Price '{5}', Delta Price '{6}'",
group.ToList()[i].day.ToString("yyyy-MM-dd"),
group.ToList()[i + 1].day.ToString("yyyy-MM-dd"),
group.ToList()[i].name,
group.ToList()[i].kind,
group.ToList()[i].price.ToString(),
group.ToList()[i + 1].price.ToString(),
(group.ToList()[i + 1].price - group.ToList()[i].price).ToString()
);
}
}
}
}
}