The code below reads a column of a csv file.
It does that properly.
I want to copy var records to a decimal array.
I am using csv helper.
How best to do it?
using (var reader = new StreamReader(filename))
using (var csv = new CsvReader(reader, CultureInfo.InvariantCulture))
{
var records = new List<Filevalues>();
csv.Read();
csv.ReadHeader();
while (csv.Read())
{
var record = new Filevalues
{
File_vals = csv.GetField<decimal>("File_vals"),
};
records.Add(record);
}
}
public class Filevalues
{
public decimal File_vals{ get; set; }
}
At its most simple:
using var csv = new CsvReader(new StreamReader(filename), CultureInfo.InvariantCulture));
csv.GetRecords<Filevalues>().Select(f => f.File_vals).ToArray();
(I think I'd ToList() it instead of array, and work with the List)
If the CSV is so simple that it's just a list of decimals, I might skip using a CSV library all together:
File.ReadLines(path).Skip(1).Select(decimal.Parse).ToArray();
Either:
using (var reader = new StreamReader(filename))
using (var csv = new CsvReader(reader, CultureInfo.InvariantCulture))
{
var listOfDecimals = new List<decimal>();
csv.Read();
csv.ReadHeader();
while (csv.Read())
{
listOfDecimals.Add(csv.GetField<decimal>("File_vals"));
}
var arrayOfDecimals = listOfDecimals.ToArray();
}
ToArray: https://learn.microsoft.com/en-us/dotnet/api/system.linq.enumerable.toarray
Or:
using (var reader = new StreamReader(filename))
using (var csv = new CsvReader(reader, CultureInfo.InvariantCulture))
{
var records = new List<Filevalues>();
csv.Read();
csv.ReadHeader();
while (csv.Read())
{
var record = new Filevalues
{
File_vals = csv.GetField<decimal>("File_vals"),
};
records.Add(record);
}
var arrayOfDecimals = records.Select(x => x.File_vals).ToArray();
}
public class Filevalues
{
public decimal File_vals{ get; set; }
}
LINQ Projection: https://learn.microsoft.com/en-us/dotnet/csharp/programming-guide/concepts/linq/projection-operations
Related
How I can directly jump to a row of a csv file ? Please see the below shown structure of csv. I want to jump directly to the row 3 (skip 0,1,2 rows) using csvhelper class ? RowId is a column in the csv file.
RowId Name
----- ----
0 Raju
1 Sabu
2 Ravi
3 Lal
4 Babu
Here is how I read csv file :
CsvConfiguration csvConfiguration = new CsvConfiguration(CultureInfo.InvariantCulture)
{
HasHeaderRecord = true,
Delimiter = ",",
PrepareHeaderForMatch = args => args.Header.ToUpper(),
IgnoreBlankLines = true,
IgnoreReferences = true,
MissingFieldFound = null,
UseNewObjectForNullReferenceMembers = true
};
CsvReader csv = new CsvReader(File.OpenText(FileNameWithPath), csvConfiguration);
csv.Read();
csv.ReadHeader();
while (csv.Read())
{
string d0 = csv.GetField<string>("Name");
var d5 = csv.GetField<int>("Class");
}
And thiscsv file contains more than 2000 records. So If I want to jump to a row (eg: 250 rowid) by skipping all the rows just above the rowid=230, how i can do that ?
After finding this rowid = 230, i also want to start reading from row id 230 onwards.
How we can do that using csvhelper with c#?
One way is to use ShouldSkipRecords.
void Main()
{
var s = new StringBuilder();
s.Append("RowId,Name\r\n");
s.Append("0,zero\r\n");
s.Append("1,one\r\n");
s.Append("2,two\r\n");
s.Append("3,three\r\n");
s.Append("4,four\r\n");
s.Append("5,five\r\n");
s.Append("6,six\r\n");
var config = new CsvConfiguration(CultureInfo.InvariantCulture)
{
ShouldSkipRecord = args => args.Row.Parser.Row > 1 && args.Row.GetField<int>("RowId") < 3,
};
using (var reader = new StringReader(s.ToString()))
using (var csv = new CsvReader(reader, config))
{
csv.GetRecords<Foo>().ToList().Dump();
}
}
private class Foo
{
public int RowId { get; set; }
public string Name { get; set; }
}
Another is to read manually.
void Main()
{
var s = new StringBuilder();
s.Append("RowId,Name\r\n");
s.Append("0,zero\r\n");
s.Append("1,one\r\n");
s.Append("2,two\r\n");
s.Append("3,three\r\n");
s.Append("4,four\r\n");
s.Append("5,five\r\n");
s.Append("6,six\r\n");
var config = new CsvConfiguration(CultureInfo.InvariantCulture)
{
};
using (var reader = new StringReader(s.ToString()))
using (var csv = new CsvReader(reader, config))
{
var records = new List<Foo>();
csv.Read();
csv.ReadHeader();
while (csv.Read())
{
var rowId = csv.GetField<int>("RowId");
if (rowId < 3)
{
continue;
}
records.Add(csv.GetRecord<Foo>());
}
records.Dump();
}
}
private class Foo
{
public int RowId { get; set; }
public string Name { get; set; }
}
I am trying to use CSVHelper library to write data into a MemoryStream and then generate a CSV file using that MemoryStream.
The problem is Double values get converted to weird string values when the double values have decimal points. The expected output and weird output are there at the bottom.
Is anyone know how to overcome this issue? or Is there any mistake in the below code?
public class Foo
{
public Foo()
{
}
public double valOne { get; set; }
public double valTwo { get; set; }
}
public class FooMap : ClassMap<Foo>
{
public FooMap()
{
Map(m => m.valOne).Index(0).Name("Val One");
Map(m => m.valTwo).Index(1).Name("Val Two");
}
}
var records = new List<Foo> {
new Foo{valOne = 3224.12, valTwo = 4122},
new Foo{valOne = 2030.20, valTwo = 5555},
};
var config = new CsvConfiguration(CultureInfo.CurrentCulture) { Delimiter = ",", HasHeaderRecord = true };
using (var memoryStream = new MemoryStream())
using (var writer = new StreamWriter(memoryStream))
using (var csv = new CsvWriter(writer, config))
{
csv.Context.RegisterClassMap<FooMap>();
csv.WriteHeader<Foo>();
csv.NextRecord();
foreach (var record in records)
{
csv.WriteRecord(record);
csv.NextRecord();
}
writer.Flush();
var result = Encoding.UTF8.GetString(memoryStream.ToArray());
byte[] bytes = Encoding.ASCII.GetBytes(result);
return new FileContentResult(bytes, "text/csv")
{
FileDownloadName = "Sample_Report_Name"
};
}
Expected Output:
Val One, Val Two
3224.12,4122
2030.20,5555
Weird Output:
Val One, Val Two
"3224,12",4122
"2030,20",5555
The issue is the CurrentCulture of the computer running the code uses commas instead of periods to indicate the decimal point. Using CultureInfo.InvariantCulture instead of CultureInfo.CurrentCulture should fix the formatting issue.
Also, you can simplify your code by using csv.WriteRecords(records).
var records = new List<Foo> {
new Foo{valOne = 3224.12, valTwo = 4122},
new Foo{valOne = 2030.20, valTwo = 5555},
};
var config = new CsvConfiguration(CultureInfo.CurrentCulture) { Delimiter = ",", HasHeaderRecord = true };
using (var memoryStream = new MemoryStream())
using (var writer = new StreamWriter(memoryStream))
using (var csv = new CsvWriter(writer, config))
{
csv.Context.RegisterClassMap<FooMap>();
csv.WriteRecords(records);
writer.Flush();
var result = Encoding.UTF8.GetString(memoryStream.ToArray());
byte[] bytes = Encoding.ASCII.GetBytes(result);
return new FileContentResult(bytes, "text/csv")
{
FileDownloadName = "Sample_Report_Name"
};
}
I'm a newbie. I want to get data from the CSV file-the Id and Name fields, but when I run the reading method, I get only 100 lines of an incomprehensible type: "CsvHelper.CsvReaderd__87`1[Program+Product]". I do not know how to get data from CSV, I also cannot understand where the error is.
Although the documentation says that having the same names of properties and CSV headers, you do not need to write additional configurations. However, I get the result specified above. The CSV names match the classes. Link to the documentation:https://joshclose.github.io/CsvHelper/getting-started/
reading method:
{
using (var reader = new StreamReader("C:\\Users\\Saint\\Desktop\\TaskRetail\\file.csv", Encoding.UTF8))
using (var csv = new CsvReader(reader, CultureInfo.InvariantCulture))
{
var records = csv.GetRecords<Product>();
Console.WriteLine($"{records}");
}
}
CSV is created without problems, there are two columns with Id and Name with filled rows, there are 100 rows in total:
method for creating a csv with the Id and Name fields:
using (var writer = new StreamWriter("C:\\Users\\Saint\\Desktop\\TaskRetail\\file.csv", false, Encoding.UTF8))
using (var csv = new CsvWriter(writer, CultureInfo.InvariantCulture))
{
csv.WriteRecords(products);
}
the entire code:
using CsvHelper;
using System;
using System.Collections.Generic;
using System.Globalization;
using System.IO;
using System.Net;
using System.Text;
using System.Xml;
public class Program
{
public class Product
{
public int Id { get; set; }
public string Name { get; set; }
public Product(int id, string name)
{
Id = id;
Name = name;
}
}
public const string PathToDoc = "C:/Users/Saint/Desktop/TaskRetail/yml.xml";
public static void Main(string[] args)
{
string url = "https://www.googleapis.com/drive/v3/files/1sSR9kWifwjIP5qFWcyxGCxN0-MoEd_oo?alt=media&key=AIzaSyBsW_sj1GCItGBK0vl8hr9zu1I1vTI1Meo";
string savePath = #"C:\Users\Saint\Desktop\TaskRetail\yml.xml";
WebClient client = new WebClient();
client.DownloadFile(url, savePath);
Research();
}
public static void Research()
{
Encoding.RegisterProvider(CodePagesEncodingProvider.Instance);
var document = new XmlDocument();
document.Load(PathToDoc);
var xmlDoc = document.SelectNodes("/yml_catalog/shop/offers/offer");
var count = xmlDoc.Count;
var products = new List<Product>();
Console.WriteLine($"Offers count: {count}");
for (var i = 0; i < count; i++)
{
var element = xmlDoc.Item(i);
var id = int.Parse(element.Attributes.GetNamedItem("id").Value);
var name = element.SelectSingleNode("name").InnerText;
var product = new Product(id, name);
//Console.WriteLine($"Id: {id}, name: {name}");
products.Add(product);
using (var writer = new StreamWriter("C:\\Users\\Saint\\Desktop\\TaskRetail\\file.csv", false, Encoding.UTF8))
using (var csv = new CsvWriter(writer, CultureInfo.InvariantCulture))
{
csv.WriteRecords(products);
}
var config = new CsvConfiguration(CultureInfo.InvariantCulture) { Delimiter = ",", PrepareHeaderForMatch = header => header.Header.ToLower() };
using (var reader = new StreamReader("C:\\Users\\Saint\\Desktop\\TaskRetail\\file.csv", Encoding.UTF8))
using (var csv = new CsvReader(reader, config))
{
var records = csv.GetRecords<Product>();
foreach (var record in records)
{
Console.WriteLine($"{record.Id} {record.Name}");
}
}
}
}
}
Because GetRecords() does return an object of type IEnumerable,
you have to iterate over your records to print each one of them:
foreach(var record in records)
{
Console.WriteLine($"{record.Id} {record.Name}");
}
Furthermore you have to access each property you want to print individually.
Another option would be to override the ToString() method in your Product class.
EDIT
The initial problem wasn't the correct printing of the values but the parsing of the file as I learned from this comment:
CsvHelper.HeaderValidationException: 'Header with name 'id'[0] was not found. Header with name 'name'[0] was not found.
To tackle this problem one have to make sure that the delimiter character is set correctly. This can be enforced in the config object of the CsvHelper. Furthermore to avoid casing errors the configuration can be set to ignore the casing of the headers:
var config = new CsvConfiguration(CultureInfo.InvariantCulture)
{
Delimiter = ",", // Enforce ',' as delimiter
PrepareHeaderForMatch = header => header.Header.ToLower() // Ignore casing
};
using (var csv = new CsvReader(reader, config))
{
...
}
I have this code here that works how I want, but I'm developing on .Net Standard and cannot use the Microsoft.VisualBasic.FileIO library. I would like to be able to do the same thing using CsvHelper
Is it possible with CsvHelper to, first get the header row as a string array. Second, get each row as a string array of items?
using Microsoft.VisualBasic.FileIO;
using (TextFieldParser csvParser = new TextFieldParser("FilePath"))
{
csvParser.CommentTokens = new string[] { "#" };
csvParser.SetDelimiters(new string[] { "," });
csvParser.HasFieldsEnclosedInQuotes = true;
csvParser.TrimWhiteSpace = true;
string[] fields = csvParser.ReadFields();
int arraySize = fields.Length;
int index = Array.IndexOf(fields, columnExtracted);
while (!csvParser.EndOfData)
{
fields = csvParser.ReadFields();
string column = fields[index];
Console.WriteLine(column);
}
}
I tried the following code that reads the cells but breaks the line when faced with a delimiter because of the line string Csv = string.Join("\n", result.ToArray());
Is there a way to put all the info in an array? It's already stored in a string list but I cannot retrieve the data by an index.
List<string> result = new List<string>();
string value;
using (TextReader fileReader = File.OpenText("FilePath"))
{
var csv = new CsvReader(fileReader, CultureInfo.InvariantCulture);
csv.Configuration.HasHeaderRecord = true;
while (csv.Read())
{
for (int i = 0; csv.TryGetField<string>(i, out value); i++)
{
result.Add(value);
}
if (csv.Read() == false)
break;
}
}
string Csv = string.Join("\n", result.ToArray());
EDIT
This is my new code now after the feedback. But, it seems that it's leaving out the header row when iterating through the List that stores all the records.
public List<string> HeaderColumnParser(string columnExtracted, string PathToFile)
{
List<string> result = new List<string>();
string value;
using (TextReader fileReader = File.OpenText(PathToFile))
{
var csv = new CsvReader(fileReader, CultureInfo.InvariantCulture);
csv.Read();
csv.ReadHeader();
string[] header = csv.Context.HeaderRecord;
int extractedIndex = Array.IndexOf(header, columnExtracted);
while (csv.Read())
{
string[] row = csv.Context.Record;
string column = row[extractedIndex];
result.Add(column);
}
}
return result;
This should work if you want each row as a string array.
using (MemoryStream stream = new MemoryStream())
using (StreamWriter writer = new StreamWriter(stream))
using (StreamReader reader = new StreamReader(stream))
using (CsvReader csv = new CsvReader(reader, CultureInfo.InvariantCulture))
{
writer.WriteLine("Id,Name,Column3,Column4,Column5");
writer.WriteLine("1,One,Item1,Item2,Item3");
writer.WriteLine("2,Two,Item4,Item5,Item6");
writer.Flush();
stream.Position = 0;
reader.BaseStream.Position = 0;
csv.Read();
csv.ReadHeader();
string[] header = csv.Context.HeaderRecord;
var columnExtracted = "Column3";
int extractedIndex = Array.IndexOf(header, columnExtracted);
while (csv.Read())
{
string[] row = csv.Context.Record;
var column = row[extractedIndex];
Console.WriteLine(column);
}
}
Console.ReadKey();
I am writing my data from a public class to a CSV file. As I want to append my data, I want to exclude the importing of header and only import the data from the class. My code below imports both headers and data. Hope to get help. Thanks.
Record.cs - my class
public class Record
{
public string Name
{
get; set;
}
public DateTime DateOfBirth
{
get; set;
}
}
Form1.cs - my form
public partial class Form1 : Form
{
private List<Record> records;
public Form1()
{
InitializeComponent();
records = new List<Record>();
}
private void Savetocsv_Click(object sender, EventArgs e)
{
var myDocument = Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments);
using (var writer = new StreamWriter(myDocument + "/my-data.csv", append: true))
{
using (var csv = new CsvWriter(writer))
{
csv.WriteRecords(records);
}
}
}
Using the Configuration , you can use the property HasHeaderRecord:
HasHeaderRecord :
Gets or sets a value indicating if the CSV file has a header record.
Default is true.
var records = new List<Foo>
{
new Foo { Id = 1, Name = "one" },
new Foo { Id = 1, Name = "one" },
};
using (var writer = new StreamWriter($"file.csv"))
using (var csv = new CsvWriter(writer, new Configuration { HasHeaderRecord = false }))
{
csv.WriteRecords(records);
}
Result file : "file.csv"
1;one
1;one
Or simply loop on records an write them:
var records = new List<Foo>
{
new Foo { Id = 1, Name = "one" },
new Foo { Id = 1, Name = "one" }
};
using (var writer = new StreamWriter($"file.csv"))
using (var csv = new CsvWriter(writer))
{
foreach (var record in records)
{
csv.WriteRecord(record);
csv.NextRecord();
}
}
The name of the configuration class has changed.
using (var csv = new CsvWriter(outputStream, new CsvConfiguration(CultureInfo.InvariantCulture)
{
HasHeaderRecord = false
}))
Change your writing method as following and then CsvHelper.WriterConfiguration do the trick (note HasHeaderRecord):
private void Savetocsv_Click(object sender, EventArgs e)
{
var myDocument = Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments);
using (var writer = new StreamWriter(myDocument + "/my-data.csv", append: true))
{
using (var csv = new CsvWriter(writer, new Configuration { HasHeaderRecord = false }))
{
csv.WriteRecords(records);
}
}
}
I don't know which CsvWriter you are using, but the one here has a HasHeaderRecord property that you can use to ignore or include headers.
private void Savetocsv_Click(object sender, EventArgs e)
{
var myDocument = Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments);
using (var writer = new StreamWriter(myDocument + "/my-data.csv", append: true))
{
using (var csv = new CsvWriter(writer))
{
csv.Configuration.HasHeaderRecord = true;
csv.WriteRecords(records);
}
}
}
Remove the first row from records before calling:
csv.WriteRecords(records);
(If you need to leave records unchanged, add the headers back again after calling WriteRecords(...).)
private void Savetocsv_Click(object sender, EventArgs e)
{
var myDocument = Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments);
using (var writer = new StreamWriter(myDocument + "/my-data.csv", append: true))
{
using (var csv = new CsvWriter(writer))
{
records.RemoveAt(0); // Removes the header row.
csv.WriteRecords(records);
}
}
}