How to read csv file having ',' inside a data - c#

I have some csv data:
"data1", "data2", "data2_1", "data3"
I am using csvHelper to read the data.
When I read the data and split it using separator ',' I am getting 4 records.
"data1",
"data2",
"data2_1",
"data3"
But I want 3 records as I have 3 columns
"data1",
"data2, data2_1",
"data3"
Below is code I am trying
var config = new CsvConfiguration() { HasHeaderRecord = false };
var stream = File.OpenRead(FilePath);
using (var csvReader = new CsvReader(new StreamReader(stream, Encoding.UTF8), config))
{
while (csvReader.Read()) {
var parser = csvReader.Parser;
var rowRecors = parser.RawRecord;
var splitedData = rowRecors.Split(',');
}

It seems that you are using this library: CsvHelper
Your file data is quoted meaninig that each field is enclosed in double quotes for the exact reason that some fields can contain the field separator (the comma). In this case you should inform your library what is the character used to quote your fields so it can ignore the presence of the field separator when it is inside the quotes pair.
This library offers the Configuration class that you already initialize.
You just need to add this to the constructor
var config = new CsvConfiguration()
{
HasHeaderRecord = false,
Quote = '"'
};
See the properties list at CsvHelper.Configuration

Don't split manually and instead use lib functions
// By header name
var field = csv["HeaderName"];
// Gets field by position returning string
var field = csv.GetField( 0 );
// Gets field by position returning int
var field = csv.GetField<int>( 0 );
// Gets field by header name returning bool
var field = csv.GetField<bool>( "IsTrue" );
// Gets field by header name returning object
var field = csv.GetField( typeof( bool ), "IsTrue" );
Refer reading samples

Related

Generate txt file with the specified order of the field based on the parameters of the input file

I have a question regarding logic in C# of the following. I need to generate a file where the order of the fields are specified by the input JSON. For example, I have three fields in my output:
{ID, Name, Value}
The order of these fields are specified in the JSON file i.e.
ID = 1
Name = 2
Value = 3
So, if I need to change an order of the field I just do it in my JSON file which is added to the project.
try this
using Newtonsoft.Json;
var json = "{\"FileConf\": [ { \"ID\" : 1, \"Name\" : 2, \"Value\" : 3 } ]}";
var jArray = (JArray)JObject.Parse(json)["FileConf"];
var arr = jArray.Select(a => new string[] { a["ID"].ToString(),
a["Name"].ToString(), a["Value"].ToString() });
var sb= new StringBuilder();
foreach (var item in arr)
{
sb.Append(string.Join(",",item));
sb.Append(Environment.NewLine);
}
File.WriteAllText("somefile.csv",sb.ToString());

CSV quoted values with line break inside data

This question is specific to ChoETL CSV reader
Take this example
"Header1","Header2","Header3"
"Value1","Val
ue2","Value3"
(Notepad++ screenshot)
All headers and values are quoted
There's a line break in "Value2"
I've been playing with ChoETL options, but I can't get it to work:
foreach (dynamic e in new
ChoCSVReader(#"test.csv")
.WithFirstLineHeader()
.MayContainEOLInData(true)
.MayHaveQuotedFields()
//been playing with these too
//.QuoteAllFields()
// .ConfigureHeader(c => c.IgnoreColumnsWithEmptyHeader = true)
//.AutoIncrementDuplicateColumnNames()
//.ConfigureHeader(c => c.QuoteAllHeaders = true)
//.IgnoreEmptyLine()
)
{
System.Console.WriteLine(e["Header1"]);
}
This fails with:
Missing 'Header2' field value in CSV file
The error varies depending on the reader configuration
What is the correct configuration to read this text?
It is bug in handling one of the cases (ie. header having quotes - csv2 text). Applied fix. Take the ChoETL.NETStandard.1.2.1.35-beta1 package and give it a try.
string csv1 = #"Header1,Header2,Header3
""Value1"",""Val
ue2"",""Value3""";
string csv2 = #"""Header1"",""Header2"",""Header3""
""Value1"",""Val
ue2"",""Value3""";
string csv3 = #"Header1,Header2,Header3
Value1,""Value2"",Value3";
using (var r = ChoCSVReader.LoadText(csv1)
.WithFirstLineHeader()
.MayContainEOLInData(true)
.QuoteAllFields())
r.Print();
using (var r = ChoCSVReader.LoadText(csv2)
.WithFirstLineHeader()
.MayContainEOLInData(true)
.QuoteAllFields())
r.Print();
using (var r = ChoCSVReader.LoadText(csv3)
.WithFirstLineHeader()
.MayContainEOLInData(true)
.QuoteAllFields())
r.Print();
Sample fiddle: https://dotnetfiddle.net/VubCDR

C#: Appending Json Object to a Json file already containing data

I have created an object which contains data I want to insert/append to the data currently sitting in a json file.
I have succeeded in getting to to write the data to the file however it overwrites all the data that was there originally.
What i am trying to do is append this Property to the json file whilst keeping all the original information.
This is what I have done so far:
string widthBox = Width.Text.ToString();
string heightBox = Height.Text.ToString();
string WindowSizejson = File.ReadAllText(DownloadConfigFilelocation);
dynamic WindowSizejsonObj = Newtonsoft.Json.JsonConvert.DeserializeObject(WindowSizejson);
JObject windowContent = new JObject(
new JProperty("externalSite",
new JObject(
new JProperty("webLogin",
new JObject(
new JProperty("window", "height=" + heightBox + ",width=" + widthBox + ",resizable,scrollbars")
)
)
)
)
);
This is the data currently in the json file that i need to append the above to.
( have blurred out values due to company security reasons)
You have two choices that I can think of:
1.Read the entire file into an object, add your object, and then
rewrite the entire file (poor performance)
var filePath = #"path.json";
// Read existing json data
var jsonData = System.IO.File.ReadAllText(filePath);
// De-serialize to object or create new list
var SomeObjectList= JsonConvert.DeserializeObject<List<T>>(jsonData)
?? new List<T>();
// Add any new
SomeObjectList.Add(new T()
{
Name = "..."
});
SomeObjectList.Add(new T()
{
Name = "..."
});
// edit
var first = SomeObjectList.FirstOrDefault();
first.Name = "...";
// Update json data string
jsonData = JsonConvert.SerializeObject(SomeObjectList);
System.IO.File.WriteAllText(filePath, jsonData);
Open the file read/write,
parse through until you get to the closing curly brace, then write
the remaining data, then write the close curly brace (not trivial)
Instead of messing around with JProperty, deserialize your json and append your desired data:
JObject obj = JObject.Parse(jsontext);
obj["new_prop"] = "value";//new property as per hirarchy ,same for replacing values
string newjson=obj.ToString();
it's much cleaner and easier to maintain.

CsvHelper Ignore case for header names

I have some class
public class Import
{
public DateTime Date { get; set; }
public string Category { get; set; }
}
In csv file header names can be in lowercase.
How I can ignore case while reading file?
var reader = new StreamReader(#"///");
var csv = new CsvReader(reader);
var records = csv.GetRecords<Import>().ToList();
If you are using the http://joshclose.github.io/CsvHelper/ you can provide some configuration when constructing the CsvReader or configuring it after construction.
using (var stringReader = new StringReader(yourString))
using (var csvReader = new CsvReader(stringReader))
{
// Ignore header case.
csvReader.Configuration.PrepareHeaderForMatch = (string header, int index) => header.ToLower();
return csvReader.GetRecords<Import>().ToList();
}
There is more documentation in the PrepareHeaderForMatch section at https://joshclose.github.io/CsvHelper/api/CsvHelper.Configuration/Configuration/
For more granularity there are also class mapping instructions for which can be found under here:
https://joshclose.github.io/CsvHelper/examples/configuration
Hope that helps.
In the current version of CsvHelper, you have to configure it like this:
var csvConfig = new CsvConfiguration(CultureInfo.InvariantCulture)
{
PrepareHeaderForMatch
= args => args.Header.ToLower()
};
using (var reader = new StreamReader(inputFile))
using (var csv = new CsvReader(reader, csvConfig))
{
...
}
A blog post from Mak (2022-09-26) has three different ways to configure CsvHelper.
When your CSV header names don’t match your property names exactly,
CsvHelper will throw an exception. For example, if your header name is
“title” and your property name is “Title”, it’ll throw an exception
like: HeaderValidationException: Header with name ‘Title'[0] was not
found.
If you don’t want to (or can’t) change the names to match, then you
can configure CsvHelper to map headers to properties with different
names. You have three options:
Use the [Name] attribute on properties that need it.
Use CsvConfiguration.PrepareHeaderForMatch when there’s a pattern to the
naming differences (such as a casing difference).
Use a ClassMap to explicitly declare how all properties should be mapped.
C# – Configuring CsvHelper when the header names are different from the properties

Split does not work as expected with commas

I need to write a CSV Parser I am now trying to separat the fields to manipulate them.
Sample CSV:
mitarbeiter^tagesdatum^lohnart^kostenstelle^kostentraeger^menge^betrag^belegnummer
11005^23.01.2018^1^^31810020^5,00^^
11081^23.01.2018^1^^31810020^5,00^^
As you can see, there a several empty cells.
I am doing the following:
using (CsvFileReader reader = new CsvFileReader(path))
{
CsvRow row = new CsvRow();
while (reader.ReadRow(row))
{
foreach (string s in row)
{
csvROW.Add(new aCSVROW());
string[] items = s.Split(new char[] { '^' }, StringSplitOptions.None);
csvROW[0].mitarbeiter = items[0];
csvROW[0].tagesdatum = items[1];
csvROW[0].lohnart = items[2];
csvROW[0].kostenstelle = items[3];
csvROW[0].kostentraeger = items[4];
csvROW[0].menge = items[5];
csvROW[0].betrag = items[6];
csvROW[0].belegnummer = items[7];
}
}
}
Problem:
It seems that Split stops after the comma (5,00). The separator is ^ ... is there a reason why?
I tried several things without success...
Thank you so much!
CsvFileReader reads rows from a CSV file and then strings within that row. What else do you expect the CsvFileReader to do than separating the row?
After reading the second line, row will have the contents
11005^23.01.2018^1^^31810020^5
and
00^^
When you split the first row by ^, the last entry of the resulting array will be "5". Anyway, your code will throw, because you are trying to access items exceeding the bounds of the array.
I don't know CsvFileReader. Maybe you can pass ^ as a separator and spare the splitting of the string. Anyway, you could use a StreamReader, too. This will work much more like you expected.
using (StreamReader reader = new StreamReader(path))
{
while (!reader.EndOfStream)
{
var csvLine = reader.ReadLine();
csvROW.Add(new aCSVROW());
string[] items = csvLine.Split(new char[] { '^' }, StringSplitOptions.None);
csvROW[0].mitarbeiter = items[0];
csvROW[0].tagesdatum = items[1];
csvROW[0].lohnart = items[2];
csvROW[0].kostenstelle = items[3];
csvROW[0].kostentraeger = items[4];
csvROW[0].menge = items[5];
csvROW[0].betrag = items[6];
csvROW[0].belegnummer = items[7];
}
}
Is CsvRow meant to be the data of all rows, or of one row? Because as it is, you keep adding a new aCSVROW object into csvROW for each read line, but you keep replacing the data on just csvROW[0], the first inserted aCSVROW. This means that in the end, you will have a lot of rows that all have no data in them, except for the one on index 0, that had its properties overwritten on each iteration, and ends up containing the data of the last read row.
Also, despite using a CsvReader class, you are using plain normal String.Split to actually separate the fields. Surely that's what the CsvReader class is for?
Personally, I always use the TextFieldParser, from the Microsoft.VisualBasic.FileIO namespace. It has the advantage it's completely native in the .Net framework, and you can simply tell it which separator to use.
This function can get the data out of it as simple List<String[]>:
A:
Using C# to search a CSV file and pull the value in the column next to it
Once you have your data, you can paste it into objects however you want.
List<String[]> lines = SplitFile(path, textEncoding, "^");
// I assume "CsvRow" is some kind of container for multiple rows?
// Looks like pretty bad naming to me...
CsvRow allRows = new CsvRow();
foreach (String items in lines)
{
// Create new object, and add it to list.
aCSVROW row = new aCSVROW();
csvROW.Add(row);
// Fill the actual newly created object, not the first object in allRows.
// conside adding index checks here though to avoid index out of range exceptions.
row.mitarbeiter = items[0];
row.tagesdatum = items[1];
row.lohnart = items[2];
row.kostenstelle = items[3];
row.kostentraeger = items[4];
row.menge = items[5];
row.betrag = items[6];
row.belegnummer = items[7];
}
// Done. All rows added to allRows.
CsvRow row = new CsvRow();
while (reader.ReadRow(row))
{
foreach (string s in row)
{
csvROW.Add(new aCSVROW());
s.Split("^","");
csvROW[0].mitarbeiter = items[0];
csvROW[0].tagesdatum = items[1];
csvROW[0].lohnart = items[2];
csvROW[0].kostenstelle = items[3];
csvROW[0].kostentraeger = items[4];
csvROW[0].menge = items[5];
csvROW[0].betrag = items[6];
csvROW[0].belegnummer = items[7];
}
}
}

Categories