Reading CSV file and converted to transposed table - c#

I need to read from a CSV file (separated via “;”) and a new file should be created containing the transposed (rotated) table:
my input file:
The tool shall be able to receive the filename of the table from the user and load the table to transpose the content.
The tool shall be able to save the transposed table in a new file with the filename of the input file and extended with “transposed” (“filename_transposed.csv”).
my Code
public void ReadCsv()
{
// open the file "data.csv" which is a CSV file with headers
using (CsvReader csv = new CsvReader(
new StreamReader("C:\\Users\\moki\\Downloads\\Input.csv"), true))
{
int fieldCount = csv.FieldCount;
string[] headers = csv.GetFieldHeaders();
while (csv.ReadNextRecord())
{
for (int i = 0; i < fieldCount; i++)
Console.WriteLine(string.Format("{0}\n{1}",
headers[0], csv[i]) );
}
}
Console.ReadLine();
my Result

Since we're providing answers...
using System;
using System.Collections.Generic;
using System.Linq;
namespace _51306985
{
class Program
{
static List<List<string>> listOfList = new List<List<string>>();
static int longestCol = 0;
static void Main(string[] args)
{
FillTheList("M:\\StackOverflowQuestionsAndAnswers\\51306985\\testdata.csv");
PadTheList();
SpitItBackOut();
SpitItOutToAFile("M:\\StackOverflowQuestionsAndAnswers\\51306985\\testdata.csv");
Console.ReadLine();
}
private static void SpitItOutToAFile(string v)
{
string newPath = $"{System.IO.Path.GetDirectoryName(v)}\\{System.IO.Path.GetFileNameWithoutExtension(v)}_Rotated{System.IO.Path.GetExtension(v)}";
using (System.IO.StreamWriter sw = new System.IO.StreamWriter(newPath))
{
for (int i = 0; i < longestCol; i++)
{
string lineToWrite = string.Empty;
for (int b = 0; b < listOfList.Count; b++)
{
lineToWrite += $"{listOfList[b][i]},";
}
lineToWrite = lineToWrite.Substring(0, lineToWrite.Length - 1);//remove the hanging comma
if (lineToWrite != "")
{
sw.WriteLine(lineToWrite);
}
}
}
}
private static void SpitItBackOut()
{
for (int i = 0; i < longestCol; i++)
{
string lineToWrite = string.Empty;
for (int b = 0; b < listOfList.Count; b++)
{
lineToWrite += $"{listOfList[b][i]},";
}
lineToWrite = lineToWrite.Substring(0, lineToWrite.Length - 1);//remove the hanging comma
if (lineToWrite != "")
{
Console.WriteLine(lineToWrite);
}
}
}
private static void PadTheList()
{
foreach (List<string> item in listOfList)
{
while (item.Count < longestCol)
{
item.Add("");
}
}
}
private static void FillTheList(string v)
{
using (System.IO.StreamReader sr = new System.IO.StreamReader(v))
{
string currentLine = string.Empty;
while ((currentLine = sr.ReadLine()) != null)
{
listOfList.Add(currentLine.Split(',').ToList());
if (listOfList.Last().Count > longestCol)
{
longestCol = listOfList.Last().Count;
}
}
}
}
}
}
Input Data
a1,b1,c1,d1,e1
a2,b2,c2,d2,e2
a3,b3,c3,d3,e3
a4,b4,c4,d4,e4
a5,b5,c5,d5,e5
a6,b6
a7,b7,c7,d7
a8,b8,c8
Output
a1,a2,a3,a4,a5,a6,a7,a8
b1,b2,b3,b4,b5,b6,b7,b8
c1,c2,c3,c4,c5,,c7,c8
d1,d2,d3,d4,d5,,d7,
e1,e2,e3,e4,e5,,,

There surely is a more efficient way but this is a easy to understand way I think:
1.) put the data into a datatable, e.g. like:
StreamReader sr1 = new StreamReader("C:\\Users\\moki\\Downloads\\Input.csv"); //create the streamreader to read the input .csv
DataTable mydata = new DataTable(); //create an empty DataTable.....
string[] arr; //....and an array in which you will store the elemnets of each line
int i = 0; //just a variable to help counting where you are in your data
bool mydatasetup = false; //a variable to check in the loop if you already added the necessary number of columns to the datatable
using (sr1)
{
while (sr1.EndOfStream == false) //read the whole file
{
string line = sr1.ReadLine(); //get a line from the file
if (line != null && line != String.Empty) //check if there is content in the line
{
arr = line.Split(';'); //split the line at each ";" and put the elements in the array
if(mydatasetup == false) //after reading the first line add as many columns to your datatable as you will need.....
{
for (int u = 0; u < arr.Length; u++)
{
mydata.Columns.Add();
}
mydatasetup = true; //...but only do this once (otherwise you wil have an unneccessary big datatable
}
mydata.Rows.Add(); //add a row in you datatable in which you will store the data of the line
for (int j = 0; j < arr.Length; j++) //go throught each element in your array and put it into your datatable
{
if (arr[j] != "")
{
mydata.Rows[i][j] = arr[j];
}
}
i = i + 1; //increase the counter so that the program knows it has to fill the data from the next line into the next row of the datatable
}
}
}
2.) Then you can loop through your datatable's columns and add each row's contents to a Stringbuilder (whereby you transpose your data) which you then save as a .csv:
StringBuilder sb = new StringBuilder(); //create a stringbuilder
for (int u = 0; u < mydata.Columns.Count; u++) //loop through the COLUMNS of your datatable....
{
for (int i = 0; i < mydata.Rows.Count; i++) //....but for each column go through each row in the datatable first
{
sb.Append(mydata.Rows[i][u].ToString()); // and add the elements to the stringbuilder - here the transposing is actually done
if (i < mydata.Rows.Count - 1) //add a deliminator after each element because you want a .csv as output again
{
sb.Append(';');
}
}
sb.AppendLine(); //add another line to your stringbuilder in which you will store the next column of your datatable
}
File.WriteAllText("C:\\Users\\moki\\Downloads\\Output.csv", sb.ToString()); //finally create the output .csv
You could of course combine these two steps.

Just in case, if anyone want to know how to do it using Cinchoo ETL with few lines of code,
string csv = #"A1;B1;C1;D1;E1
A2;B2;C2;D2;E2
A3;B3;C3;D3;E3
A4;B4;C4;D4;E4
A5;B5;C5;D5;E5
";
StringBuilder sb = new StringBuilder();
using (var p = ChoCSVReader.LoadText(csv)
.WithDelimiter(";")
.ThrowAndStopOnMissingField(false)
)
{
using (var w = new ChoCSVWriter(sb)
.WithDelimiter(";")
)
{
w.Write(p.Cast<ChoDynamicObject>().Transpose(false));
}
}
Console.WriteLine(sb.ToString());
Output:
A1;A2;A3;A4;A5
B1;B2;B3;B4;B5
C1;C2;C3;C4;C5
D1;D2;D3;D4;D5
E1;E2;E3;E4;E5

Related

Docx - Removing section of document

Is there a way to remove sections of a document where i can specify the beginning and ending tags?
i need a way that i can remove a section of the document by passing in both my start and end catches, (##DELETEBEGIN and ##DELETEEND)
for example i have this in my document:
Hello, welcome to this document
##DELETEBEGIN{Some values to check in the code}
Some text that will be removed if the value is true
##DELETEEND
Final Line
If you need to delete text from ##DELETEBEGIN to ##DELETEEND, where ##DELETEBEGIN is not at the beginning of a Paragraph and ##DELETEEND is not at the end of a Paragraph, this code should work.
DocX document = DocX.Load("C:\\Users\\phil\\Desktop\\text.docx");
bool flag = false;
List<List<string>> list1 = new List<List<string>>();
List<string> list2 = new List<string>();
foreach (Novacode.Paragraph item in document.Paragraphs)
{
//use this if you need whole text of a paragraph
string paraText = item.Text;
var result = paraText.Split(' ');
int count = 0;
list2 = new List<string>();
//use this if you need word by word
foreach (var data in result)
{
string word = data.ToString();
if (word.Contains("##DELETEBEGIN")) flag = true;
if (word.Contains("##DELETEEND"))
{
flag = false;
list2.Add(word);
}
if (flag) list2.Add(word);
count++;
}
list1.Add(list2);
}
for (int i = 0; i < list1.Count(); i++)
{
string temp = "";
for (int y = 0; y < list1[i].Count(); y++)
{
if (y == 0)
{
temp = list1[i][y];
continue;
}
temp += " " + list1[i][y];
}
if (!temp.Equals("")) document.ReplaceText(temp, "");
}
document.Save();
I have to give some credit to this post for looping through each word.
I think i have found a solution to this, at least it works for me, please let me know if there is anything i can do better:
the deleteCommand would be the ##DELETEBEGIN string and the deleteEndCommand would be the ##DELETEEND
private void RemoveSection(DocX doc, string deleteCommand, string deleteEndCommand)
{
try
{
int deleteStart = 0;
int deleteEnd = 0;
//Get the array of the paragraphs containing the start and end catches
for (int i = 0; i < doc.Paragraphs.Count; i++)
{
if (doc.Paragraphs[i].Text.Contains(deleteCommand))
deleteStart = i;
if (doc.Paragraphs[i].Text.Contains(deleteEndCommand))
deleteEnd = i;
}
if (deleteStart > 0 && deleteEnd > 0)
{
//delete from the paraIndex as the arrays will shift when a paragraph is deleted
int paraIndex = deleteStart;
for (int i = deleteStart; i <= deleteEnd; i++)
{
doc.RemoveParagraphAt(paraIndex);
}
}
}
catch (Exception ex)
{
MessageBox.Show(ex.ToString());
}
}

c# Add Specific columns from a TEXT file to DataGridView

Hello Everyone,
As shown in the above image I want to add the decimal numbers column wise from a text file to datagrid control.
Following is my code snippet
List<string> str = new List<string>();
String st = "";
int k = 0;
string[] s ;
//Path to write contents to text file
string filename = #"E:\Vivek\contentcopy\clientlist.txt";
Form.CheckForIllegalCrossThreadCalls = false;
OpenFileDialog ofd = new OpenFileDialog();
ofd.FileName = "";
ofd.ShowDialog();
st = ofd.FileName;
if (string.IsNullOrEmpty(ofd.FileName))
return;
string Name = "", No1 = "",No2="";
string[] lines = File.ReadAllLines(st).Where(sw => !string.IsNullOrWhiteSpace(sw)).ToArray();
for (int i = 0; i < lines.Length; i++)
{
if (lines[i].Contains("VENTURA SECURITIES LIMITED (NSE F&O)")) continue;
if (lines[i].Contains("ALL EXCHANGES DERIVATIVES CLIENTWISE STATEMENT AS ON 16-05-2012")) continue;
if (lines[i].Contains("-------------------------------------------------------")) continue;
s = lines[i].Split(' ');
if (s[0] == "PARTY" || s[0] == "") continue;
int z;
Name = "";
for (z = 1; z < s.Length; z++)
{
if (s[z] == "") continue;
if (s[z].Contains('.'))
{
No1+=s[z]+" ";
No2 = No1 + " ";
}
else
{
Name += s[z];
str.Add(s[0]+" "+Name);
}
}
dataGridView1.Rows.Add();
dataGridView1.Rows[k].Cells[0].Value = s[0];
dataGridView1.Rows[k].Cells[1].Value = Name;
dataGridView1.Rows[k].Cells[2].Value = No1;
dataGridView1.Rows[k].Cells[3].Value = No2;
k++;
}
File.WriteAllLines(filename, str);
dataGridView1.ReadOnly = true;
}
The line No1=s[z] directly takes the last column values ie 46,123.19 and so on.I want to fetch each column from the text file and store it in a string variable and then assign it to the datagrid view
I hope my doubt is clear.If not please let me know
Here is the simplest Solution:
Add a DataGrid View to Form and add a Button:
private void button1_Click(object sender, EventArgs e)
{
ReadAndFileter();
}
private void ReadAndFileter()
{
try
{
using(System.IO.StreamReader reader = new System.IO.StreamReader("file.txt"))
{
string line;
string []array;
int rowcount= 0;
decimal number;
string[] separators = { "\t", " " };
int columnCount = 0;
while ((line = reader.ReadLine()) != null)
{
array = line.Split(separators, StringSplitOptions.RemoveEmptyEntries);
dataGridView1.Rows.Add();
foreach (string str in array)
{
if (Decimal.TryParse(str,out number))
{
dataGridView1.Rows[rowcount].Cells[columnCount++].Value = number;
}
}
rowcount++;
columnCount = 0;
}
}
}
catch (Exception ex)
{
}
}
The File Contents are:
Abc 20.122 69.33 0.00 693.25 0.00
def 36.20 96.20 1.15 69.56 8.96
And the final output:
Lets say, you have for lines in your test file, then u need to do following things:
Use StreamReader.ReadLine(), to read one line at time.
Spilt the line using split(' ') and store it in a array
Remove all the empty ones from the array
Now at index 2,3,4,5,6 of the resulting array will have the string equivalent of the decimal numbers.
Repeat this for each StreamReader.ReadLine()
Hope this will help.
Your problem is that you are overwriting No1 every time you read a string, which explains why you only get the last value. What you could do is either;
Append the string:
No1 += s[z] + " ";
Which will put all the values behind eachother, seperated by a whitespace.
Or, you could make a List<String> and add each value to the list, meaning you have them stored seperated:
List<String> values = new List<String>();
foreach(...)
{
if (s[z] == "") continue;
if (s[z].Contains('.'))
{
values.Add(s[z])
}
else
{
Name += s[z];
str.Add(s[0] + " " + Name);
}
}
You can thereafter loop through the list and add each value to a row. Considering your code piece;
int i = 2;
foreach(string value in values)
{
dataGridView1.Rows[k].Cells[i].Value = value;
i++;
}
This should work.
Hope this helps.
Here is edited code: but for future I must suggest to give a try at least..
private void ReadAndFileter1()
{
try
{
using (System.IO.StreamReader reader = new System.IO.StreamReader("file.txt"))
{
string line;
string[] array;
int rowcount = 0;
decimal number;
string[] separators = { "\t", " " };
int columnCount = 1;
string[] lines = File.ReadAllLines("file.txt");
for (int i = 0; i < lines.Length; i++)
{
if (lines[i].Contains("VENTURA SECURITIES LIMITED (NSE F&O)")) continue;
if (lines[i].Contains("ALL EXCHANGES DERIVATIVES CLIENTWISE STATEMENT AS ON 16-05-2012")) continue;
if (lines[i].Contains("-------------------------------------------------------")) continue;
array = lines[i].Split(separators, StringSplitOptions.RemoveEmptyEntries);
if (array[0] == "PARTY" || array[0] == "") continue;
dataGridView1.Rows.Add();
foreach (string str in array)
{
if (Decimal.TryParse(str, out number))
{
dataGridView1.Rows[rowcount].Cells[columnCount++].Value = number;
}
}
dataGridView1.Rows[rowcount].Cells[0].Value = array[0];
rowcount++;
columnCount = 1;
}
}
}
catch (Exception ex)
{
MessageBox.Show(ex.Message);
}
}
}
Here it is:
static void Main(string[] args)
{
Decimal result;
string[] splitchar = new string[]{" "};
using(StreamReader reader = new StreamReader(#"C:\Users\Dell\Desktop\input.txt"))
{
while(!reader.EndOfStream)
{
string[] splittedArray = reader.ReadLine().Split(splitchar, StringSplitOptions.RemoveEmptyEntries).Where(x => Decimal.TryParse(x, out result)).ToArray();
// put your code here to get insert the values in datagrid
}
}
}

Replace a Line with another Line of the Same Text file in C#

I would like to replace lines of a text with the existing lines of the file based on some data. I have developed some code blocks but it didn't work.
My Text File is Like This:-
g_start-fd,g_start-cnst,g_start-eq,mv-mv_size,mv-mv_alloy,mv-mv_argmt,mv-mv_ps,xfrmr-kva,g_end-line_t,g_end-str_num,g_end-cmt,g_end-str_coord-Latitude,g_end-str_coord-Longitude
28F1Y,oh,mv oh,120,al,oh_3,P45R24,,i,P45R25,,9.53725695,-0.86668464
28F1Y,oh,mv oh,120,al,oh_3,P45R25,,i,P45R42,,9.5468355,-0.85948875
28F1Y,oh,mv oh,120,al,oh_3,P45R42,,i,P45R49,,9.55073989,-0.85625858
28F1Y,oh,mv oh,120,al,oh_3,P45R49,,a,P45R25,,,
28F1Y,oh,mv oh,120,al,oh_3,P45R54,,i,P45R55,,9.5544981,-0.85359626
28F1Y,oh,mv xfrmr,120,al,oh_3,P45R55,5000,e,P45R56,Substation,9.5549907,-0.85303108
28F1Y,ug,mv,185,al,xlpe_3,P45R56,,e,P45R55,,,
28F1Y,ug,mv,185,al,xlpe_3,P45R57,,s,P45R58,Take off from ring main,9.55387622,-0.8538622
28F1Y,oh,mv oh,120,al,oh_3,P45R58,,a,P45R73,,9.54513187,-0.86060037
28F1Y,oh,mv oh,120,al,oh_3,P45R73,,a,P45R77,,9.5417936,-0.86098952
28F1Y,oh,mv oh,120,al,oh_3,P45R77,,a,P45R80,,9.54144045,-0.85857346
28F1Y,oh,mv oh,120,al,oh_3,P45R80,,a,P45R86,,9.53675765,-0.85935176
28F1Y,oh,mv,120,al,oh_3,P45R86,,e,P45R80,,,
My Application stops working when I run this code:
string fileName1 = "D:\\WriteTextWork\\Line1.txt"; ;
OpenFileDialog pfdg = new OpenFileDialog();
if (pfdg.ShowDialog() == DialogResult.OK)
{
fileName1 = pfdg.FileName;
}
if (File.Exists(fileName1))
{
StreamReader SR = new StreamReader(fileName1);
string Data = null;
int count = 0;
while ((Data = SR.ReadLine()) != null)
{
count++;
if (count > 1)
{
string CopyText = "";
String[] SplitData = Data.Split(',');
if (SplitData[9] != null && SplitData[11] != null)
{
CopyText = Data;
string data1 = SR.ReadLine();
//MessageBox.Show(CopyText);
}
using (StreamReader SR1 = new StreamReader(fileName1))
{
//var SW = new StreamWriter(resultString1);
string line;
while ((line = SR1.ReadLine()) != null)
{
//String TrimData2 = line.Trim();
String[] SplitText = line.Split(',');
if (SplitText[9] == SplitData[9] && SplitText[11] == null)
{
using (StreamWriter SW = new StreamWriter(resultString1))
{
SW.WriteLine(CopyText);
MessageBox.Show(CopyText);
SW.Close();
}
}
}
SR1.Close();
}
}
}
}
I made an event (btn_proceed_Click) to proceed the method, you can place him (method) anywhere you want. Here is the full code, just replace it with yours:
private void btn_proceed_Click(object sender, EventArgs e)
{
// This dictionary contains indices of rows we need to replace.
Dictionary<int, int> replaceable = new Dictionary<int, int>();
replaceable.Add(4, 1);
replaceable.Add(7, 5);
replaceable.Add(13, 11);
string input = String.Empty;
OpenFileDialog pfdg = new OpenFileDialog();
if (pfdg.ShowDialog() == DialogResult.OK)
{
input = pfdg.FileName;
}
// I placed the result into the another file called result.txt. You can use output path as same as input to overwrite the file.
ReplaceLines(replaceable, input, #"C:\Users\Wallstrider\Documents\Visual Studio 2010\Projects\result.txt", 9);
}
/// <summary>
/// Replaces lines of the file depends on 9th item is exist.
/// </summary>
/// <param name="replaceable">Rows incides to replace.</param>
/// <param name="input_path">Path to the input file.</param>
/// <param name="output_path">Path to the output file.</param>
/// <param name="has_value_index">Index of a split data value in the row.</param>
private void ReplaceLines(Dictionary<int, int> replaceable, string input_path, string output_path, int has_value_index)
{
if (File.Exists(input_path))
{
string file;
file = new StreamReader(input_path).ReadToEnd();
string[] lines = file.Split(new char[] { '\n' });
List<string[]> split_data = new List<string[]>();
for (int i = 0; i < lines.Length; i++)
split_data.Add(lines[i].Split(','));
List<int> allowed_for_replace_indices = new List<int>();
List<int> not_allowed_for_replace_indices = new List<int>();
// Check if the row has the value of 9th item then we are allowed to replace rows.
for (int i = 1; i < split_data.Count; i++)
{
if (split_data[i][has_value_index] != String.Empty)
allowed_for_replace_indices.Add(i);
else
not_allowed_for_replace_indices.Add(i);
}
List<int> rows_replaced = new List<int>();
List<int> rows_not_replaced = new List<int>();
// Loop through our replaceable indices dictionary.
for (int i = 0; i < replaceable.Count; i++)
{
int key = replaceable.ElementAt(i).Key;
int value = replaceable.ElementAt(i).Value;
// if both rows have 9th item then we can start replacement.
if (allowed_for_replace_indices.Contains(key) && allowed_for_replace_indices.Contains(value))
{
string temp = lines[value];
lines[value] = lines[key];
lines[key] = temp;
rows_replaced.Add(key);
rows_replaced.Add(value);
}
else
{
rows_not_replaced.Add(key);
rows_not_replaced.Add(value);
}
}
using (StreamWriter sw = new StreamWriter(output_path))
{
for (int i = 0; i < lines.Length; i++)
sw.WriteLine(lines[i]);
sw.Flush();
sw.Close();
}
MessageBox.Show("Rows replaced: " + String.Join("; ", rows_replaced.ToArray()) + " .\nRows not replaced: " + String.Join("; ", rows_not_replaced.ToArray()) + ".\nComplete.");
}
}
The following code will replace (I count from zero) 4, 1; 7, 5; 13, 11; rows if each of has the 9th item if I understand your logic properly.
To force your code work just replace:
if (SplitText[9] == SplitData[9] && SplitText[11] == null)
To:
if (SplitText[9] == SplitData[9] && SplitText[11] == String.Empty)
Because SplitText[11] will never be a null in all situations within the file you have shared.

How to write to csv file with StringBuilder ignoring the first row/line of CSV?

Since the first row of this CSV file already includes the column names, so I just want to ignore the first line and start to write from second line with StringBuilder.
StringBuilder sb = new StringBuilder();
foreach (DataRow dr in distinctValues.Rows)
{
vendor = dr.ItemArray[0].ToString();
for (int i = 0; i <= ds.Tables[0].Rows.Count - 1; i++)
{
if (vendor == ds.Tables[0].Rows[i]["VendorCode"].ToString())
{
for (int j = 0; j <= ds.Tables[0].Columns.Count - 1; j++)
{
if (j != 0)
sb.Append(",");
sb.Append(ds.Tables[0].Rows[i][ds.Tables[0].Columns[j]]);
}
sb.AppendLine();
}
}
File.WriteAllText(#csvFile, sb.ToString());
}
At last i understand that what i need is not to write a file but edit a file. so a changing of mind finally gives the answer.
File.AppendAllText is what i really need.
Try
File.AppendText("pathtofile")
or
FileMode.Append via FileStream
If the case of File.AppendText, all your calls to sb.AppendLine() would become sw.WriteLine() in the example below:
// This text is always added, making the file longer over time
// if it is not deleted.
using (StreamWriter sw = File.AppendText(path))
{
sw.WriteLine("This");
sw.WriteLine("is Extra");
sw.WriteLine("Text");
}
Something like this?
class Program
{
private const string csv ="Header\r\nLine1\r\nLine2";
static void Main(string[] args)
{
StringReader reader = new StringReader(csv);
StringBuilder builder = new StringBuilder();
bool header = true;
while (true)
{
string line = reader.ReadLine();
if(header)
{
header = false;
continue;
}
if (line == null)
break;
builder.AppendLine(line);
}
Console.WriteLine(builder.ToString());
Console.ReadLine();
}
}
HTH

Reading CSV file and storing values into an array

I am trying to read a *.csv-file.
The *.csv-file consist of two columns separated by semicolon (";").
I am able to read the *.csv-file using StreamReader and able to separate each line by using the Split() function. I want to store each column into a separate array and then display it.
Is it possible to do that?
You can do it like this:
using System.IO;
static void Main(string[] args)
{
using(var reader = new StreamReader(#"C:\test.csv"))
{
List<string> listA = new List<string>();
List<string> listB = new List<string>();
while (!reader.EndOfStream)
{
var line = reader.ReadLine();
var values = line.Split(';');
listA.Add(values[0]);
listB.Add(values[1]);
}
}
}
My favourite CSV parser is one built into .NET library. This is a hidden treasure inside Microsoft.VisualBasic namespace.
Below is a sample code:
using Microsoft.VisualBasic.FileIO;
var path = #"C:\Person.csv"; // Habeeb, "Dubai Media City, Dubai"
using (TextFieldParser csvParser = new TextFieldParser(path))
{
csvParser.CommentTokens = new string[] { "#" };
csvParser.SetDelimiters(new string[] { "," });
csvParser.HasFieldsEnclosedInQuotes = true;
// Skip the row with the column names
csvParser.ReadLine();
while (!csvParser.EndOfData)
{
// Read current line fields, pointer moves to the next line.
string[] fields = csvParser.ReadFields();
string Name = fields[0];
string Address = fields[1];
}
}
Remember to add reference to Microsoft.VisualBasic
More details about the parser is given here: http://codeskaters.blogspot.ae/2015/11/c-easiest-csv-parser-built-in-net.html
LINQ way:
var lines = File.ReadAllLines("test.txt").Select(a => a.Split(';'));
var csv = from line in lines
select (from piece in line
select piece);
^^Wrong - Edit by Nick
It appears the original answerer was attempting to populate csv with a 2 dimensional array - an array containing arrays. Each item in the first array contains an array representing that line number with each item in the nested array containing the data for that specific column.
var csv = from line in lines
select (line.Split(',')).ToArray();
Just came across this library: https://github.com/JoshClose/CsvHelper
Very intuitive and easy to use. Has a nuget package too which made is quick to implement: https://www.nuget.org/packages/CsvHelper/27.2.1. Also appears to be actively maintained which I like.
Configuring it to use a semi-colon is easy: https://github.com/JoshClose/CsvHelper/wiki/Custom-Configurations
You can't create an array immediately because you need to know the number of rows from the beginning (and this would require to read the csv file twice)
You can store values in two List<T> and then use them or convert into an array using List<T>.ToArray()
Very simple example:
var column1 = new List<string>();
var column2 = new List<string>();
using (var rd = new StreamReader("filename.csv"))
{
while (!rd.EndOfStream)
{
var splits = rd.ReadLine().Split(';');
column1.Add(splits[0]);
column2.Add(splits[1]);
}
}
// print column1
Console.WriteLine("Column 1:");
foreach (var element in column1)
Console.WriteLine(element);
// print column2
Console.WriteLine("Column 2:");
foreach (var element in column2)
Console.WriteLine(element);
N.B.
Please note that this is just a very simple example. Using string.Split does not account for cases where some records contain the separator ; inside it.
For a safer approach, consider using some csv specific libraries like CsvHelper on nuget.
I usually use this parser from codeproject, since there's a bunch of character escapes and similar that it handles for me.
Here is my variation of the top voted answer:
var contents = File.ReadAllText(filename).Split('\n');
var csv = from line in contents
select line.Split(',').ToArray();
The csv variable can then be used as in the following example:
int headerRows = 5;
foreach (var row in csv.Skip(headerRows)
.TakeWhile(r => r.Length > 1 && r.Last().Trim().Length > 0))
{
String zerothColumnValue = row[0]; // leftmost column
var firstColumnValue = row[1];
}
If you need to skip (head-)lines and/or columns, you can use this to create a 2-dimensional array:
var lines = File.ReadAllLines(path).Select(a => a.Split(';'));
var csv = (from line in lines
select (from col in line
select col).Skip(1).ToArray() // skip the first column
).Skip(2).ToArray(); // skip 2 headlines
This is quite useful if you need to shape the data before you process it further (assuming the first 2 lines consist of the headline, and the first column is a row title - which you don't need to have in the array because you just want to regard the data).
N.B. You can easily get the headlines and the 1st column by using the following code:
var coltitle = (from line in lines
select line.Skip(1).ToArray() // skip 1st column
).Skip(1).Take(1).FirstOrDefault().ToArray(); // take the 2nd row
var rowtitle = (from line in lines select line[0] // take 1st column
).Skip(2).ToArray(); // skip 2 headlines
This code example assumes the following structure of your *.csv file:
Note: If you need to skip empty rows - which can by handy sometimes, you can do so by inserting
where line.Any(a=>!string.IsNullOrWhiteSpace(a))
between the from and the select statement in the LINQ code examples above.
You can use Microsoft.VisualBasic.FileIO.TextFieldParser dll in C# for better performance
get below code example from above article
static void Main()
{
string csv_file_path=#"C:\Users\Administrator\Desktop\test.csv";
DataTable csvData = GetDataTabletFromCSVFile(csv_file_path);
Console.WriteLine("Rows count:" + csvData.Rows.Count);
Console.ReadLine();
}
private static DataTable GetDataTabletFromCSVFile(string csv_file_path)
{
DataTable csvData = new DataTable();
try
{
using(TextFieldParser csvReader = new TextFieldParser(csv_file_path))
{
csvReader.SetDelimiters(new string[] { "," });
csvReader.HasFieldsEnclosedInQuotes = true;
string[] colFields = csvReader.ReadFields();
foreach (string column in colFields)
{
DataColumn datecolumn = new DataColumn(column);
datecolumn.AllowDBNull = true;
csvData.Columns.Add(datecolumn);
}
while (!csvReader.EndOfData)
{
string[] fieldData = csvReader.ReadFields();
//Making empty value as null
for (int i = 0; i < fieldData.Length; i++)
{
if (fieldData[i] == "")
{
fieldData[i] = null;
}
}
csvData.Rows.Add(fieldData);
}
}
}
catch (Exception ex)
{
}
return csvData;
}
Hi all, I created a static class for doing this.
+ column check
+ quota sign removal
public static class CSV
{
public static List<string[]> Import(string file, char csvDelimiter, bool ignoreHeadline, bool removeQuoteSign)
{
return ReadCSVFile(file, csvDelimiter, ignoreHeadline, removeQuoteSign);
}
private static List<string[]> ReadCSVFile(string filename, char csvDelimiter, bool ignoreHeadline, bool removeQuoteSign)
{
string[] result = new string[0];
List<string[]> lst = new List<string[]>();
string line;
int currentLineNumner = 0;
int columnCount = 0;
// Read the file and display it line by line.
using (System.IO.StreamReader file = new System.IO.StreamReader(filename))
{
while ((line = file.ReadLine()) != null)
{
currentLineNumner++;
string[] strAr = line.Split(csvDelimiter);
// save column count of dirst line
if (currentLineNumner == 1)
{
columnCount = strAr.Count();
}
else
{
//Check column count of every other lines
if (strAr.Count() != columnCount)
{
throw new Exception(string.Format("CSV Import Exception: Wrong column count in line {0}", currentLineNumner));
}
}
if (removeQuoteSign) strAr = RemoveQouteSign(strAr);
if (ignoreHeadline)
{
if(currentLineNumner !=1) lst.Add(strAr);
}
else
{
lst.Add(strAr);
}
}
}
return lst;
}
private static string[] RemoveQouteSign(string[] ar)
{
for (int i = 0;i< ar.Count() ; i++)
{
if (ar[i].StartsWith("\"") || ar[i].StartsWith("'")) ar[i] = ar[i].Substring(1);
if (ar[i].EndsWith("\"") || ar[i].EndsWith("'")) ar[i] = ar[i].Substring(0,ar[i].Length-1);
}
return ar;
}
}
I have spend few hours searching for a right library, but finally I wrote my own code :)
You can read file (or database) with whatever tools you want and then apply the following routine to each line:
private static string[] SmartSplit(string line, char separator = ',')
{
var inQuotes = false;
var token = "";
var lines = new List<string>();
for (var i = 0; i < line.Length; i++) {
var ch = line[i];
if (inQuotes) // process string in quotes,
{
if (ch == '"') {
if (i<line.Length-1 && line[i + 1] == '"') {
i++;
token += '"';
}
else inQuotes = false;
} else token += ch;
} else {
if (ch == '"') inQuotes = true;
else if (ch == separator) {
lines.Add(token);
token = "";
} else token += ch;
}
}
lines.Add(token);
return lines.ToArray();
}
var firstColumn = new List<string>();
var lastColumn = new List<string>();
// your code for reading CSV file
foreach(var line in file)
{
var array = line.Split(';');
firstColumn.Add(array[0]);
lastColumn.Add(array[1]);
}
var firstArray = firstColumn.ToArray();
var lastArray = lastColumn.ToArray();
Here's a special case where one of data field has semicolon (";") as part of it's data in that case most of answers above will fail.
Solution in that case will be
string[] csvRows = System.IO.File.ReadAllLines(FullyQaulifiedFileName);
string[] fields = null;
List<string> lstFields;
string field;
bool quoteStarted = false;
foreach (string csvRow in csvRows)
{
lstFields = new List<string>();
field = "";
for (int i = 0; i < csvRow.Length; i++)
{
string tmp = csvRow.ElementAt(i).ToString();
if(String.Compare(tmp,"\"")==0)
{
quoteStarted = !quoteStarted;
}
if (String.Compare(tmp, ";") == 0 && !quoteStarted)
{
lstFields.Add(field);
field = "";
}
else if (String.Compare(tmp, "\"") != 0)
{
field += tmp;
}
}
if(!string.IsNullOrEmpty(field))
{
lstFields.Add(field);
field = "";
}
// This will hold values for each column for current row under processing
fields = lstFields.ToArray();
}
The open-source Angara.Table library allows to load CSV into typed columns, so you can get the arrays from the columns. Each column can be indexed both by name or index. See http://predictionmachines.github.io/Angara.Table/saveload.html.
The library follows RFC4180 for CSV; it enables type inference and multiline strings.
Example:
using System.Collections.Immutable;
using Angara.Data;
using Angara.Data.DelimitedFile;
...
ReadSettings settings = new ReadSettings(Delimiter.Semicolon, false, true, null, null);
Table table = Table.Load("data.csv", settings);
ImmutableArray<double> a = table["double-column-name"].Rows.AsReal;
for(int i = 0; i < a.Length; i++)
{
Console.WriteLine("{0}: {1}", i, a[i]);
}
You can see a column type using the type Column, e.g.
Column c = table["double-column-name"];
Console.WriteLine("Column {0} is double: {1}", c.Name, c.Rows.IsRealColumn);
Since the library is focused on F#, you might need to add a reference to the FSharp.Core 4.4 assembly; click 'Add Reference' on the project and choose FSharp.Core 4.4 under "Assemblies" -> "Extensions".
I have been using csvreader.com(paid component) for years, and I have never had a problem. It is solid, small and fast, but you do have to pay for it. You can set the delimiter to whatever you like.
using (CsvReader reader = new CsvReader(s) {
reader.Settings.Delimiter = ';';
reader.ReadHeaders(); // if headers on a line by themselves. Makes reader.Headers[] available
while (reader.ReadRecord())
... use reader.Values[col_i] ...
}
I am just student working on my master's thesis, but this is the way I solved it and it worked well for me. First you select your file from directory (only in csv format) and then you put the data into the lists.
List<float> t = new List<float>();
List<float> SensorI = new List<float>();
List<float> SensorII = new List<float>();
List<float> SensorIII = new List<float>();
using (OpenFileDialog dialog = new OpenFileDialog())
{
try
{
dialog.Filter = "csv files (*.csv)|*.csv";
dialog.Multiselect = false;
dialog.InitialDirectory = ".";
dialog.Title = "Select file (only in csv format)";
if (dialog.ShowDialog() == DialogResult.OK)
{
var fs = File.ReadAllLines(dialog.FileName).Select(a => a.Split(';'));
int counter = 0;
foreach (var line in fs)
{
counter++;
if (counter > 2) // Skip first two headder lines
{
this.t.Add(float.Parse(line[0]));
this.SensorI.Add(float.Parse(line[1]));
this.SensorII.Add(float.Parse(line[2]));
this.SensorIII.Add(float.Parse(line[3]));
}
}
}
}
catch (Exception exc)
{
MessageBox.Show(
"Error while opening the file.\n" + exc.Message,
this.Text,
MessageBoxButtons.OK,
MessageBoxIcon.Error
);
}
}
This is my 2 simple static methods to convert text from csv file to List<List<string>> and vice versa. Each method use row convertor.
This code should take into account all the possibilities of the csv file. You can define own csv separator and this methods try to correct escape double 'quote' char, and deals with the situation when all text in quotes are one cell and csv separator is inside quoted string including multiple lines in one cell and can ignore empty rows.
Last method is only for testing. So you can ignore it, or test your own, or others solution with this test method :). For testing I used this hard csv with 2 rows on 4 lines:
0,a,""bc,d
"e, f",g,"this,is, o
ne ""lo
ng, cell""",h
This is final code. For simplicity, I removed all try catch blocks.
using System;
using System.Collections.Generic;
using System.Linq;
public static class Csv {
public static string FromListToString(List<List<string>> csv, string separator = ",", char quotation = '"', bool returnFirstRow = true)
{
string content = "";
for (int row = 0; row < csv.Count; row++) {
content += (row > 0 ? Environment.NewLine : "") + RowFromListToString(csv[row], separator, quotation);
}
return content;
}
public static List<List<string>> FromStringToList(string content, string separator = ",", char quotation = '"', bool returnFirstRow = true, bool ignoreEmptyRows = true)
{
List<List<string>> csv = new List<List<string>>();
string[] rows = content.Split(new string[] { Environment.NewLine }, StringSplitOptions.None);
if (rows.Length <= (returnFirstRow ? 0 : 1)) { return csv; }
List<string> csvRow = null;
for (int rowIndex = 0; rowIndex < rows.Length; rowIndex++) {
(List<string> row, bool rowClosed) = RowFromStringToList(rows[rowIndex], csvRow, separator, quotation);
if (rowClosed) { if (!ignoreEmptyRows || row.Any(rowItem => rowItem.Length > 0)) { csv.Add(row); csvRow = null; } } // row ok, add to list
else { csvRow = row; } // not fully created, continue
}
if (!returnFirstRow) { csv.RemoveAt(0); } // remove header
return csv;
}
public static string RowFromListToString(List<string> csvData, string separator = ",", char quotation = '"')
{
csvData = csvData.Select(element =>
{
if (element.Contains(quotation)) {
element = element.Replace(quotation.ToString(), quotation.ToString() + quotation.ToString());
}
if (element.Contains(separator) || element.Contains(Environment.NewLine)) {
element = "\"" + element + "\"";
}
return element;
}).ToList();
return string.Join(separator, csvData);
}
public static (List<string>, bool) RowFromStringToList(string csvRow, List<string> continueWithRow = null, string separator = ",", char quotation = '"')
{
bool rowClosed = true;
if (continueWithRow != null && continueWithRow.Count > 0) {
// in previous result quotation are fixed so i need convert back to double quotation
string previousCell = quotation.ToString() + continueWithRow.Last().Replace(quotation.ToString(), quotation.ToString() + quotation.ToString()) + Environment.NewLine;
continueWithRow.RemoveAt(continueWithRow.Count - 1);
csvRow = previousCell + csvRow;
}
char tempQuote = (char)162;
while (csvRow.Contains(tempQuote)) { tempQuote = (char)(tempQuote + 1); }
char tempSeparator = (char)(tempQuote + 1);
while (csvRow.Contains(tempSeparator)) { tempSeparator = (char)(tempSeparator + 1); }
csvRow = csvRow.Replace(quotation.ToString() + quotation.ToString(), tempQuote.ToString());
if(csvRow.Split(new char[] { quotation }, StringSplitOptions.None).Length % 2 == 0) { rowClosed = !rowClosed; }
string[] csvSplit = csvRow.Split(new string[] { separator }, StringSplitOptions.None);
List<string> csvList = csvSplit
.ToList()
.Aggregate("",
(string row, string item) => {
if (row.Count((ch) => ch == quotation) % 2 == 0) { return row + (row.Length > 0 ? tempSeparator.ToString() : "") + item; }
else { return row + separator + item; }
},
(string row) => row.Split(tempSeparator).Select((string item) => item.Trim(quotation).Replace(tempQuote, quotation))
).ToList();
if (continueWithRow != null && continueWithRow.Count > 0) {
return (continueWithRow.Concat(csvList).ToList(), rowClosed);
}
return (csvList, rowClosed);
}
public static bool Test()
{
string csvText = "0,a,\"\"bc,d" + Environment.NewLine + "\"e, f\",g,\"this,is, o" + Environment.NewLine + "ne \"\"lo" + Environment.NewLine + "ng, cell\"\"\",h";
List<List<string>> csvList = new List<List<string>>() { new List<string>() { "0", "a", "\"bc", "d" }, new List<string>() { "e, f", "g", "this,is, o" + Environment.NewLine + "ne \"lo" + Environment.NewLine + "ng, cell\"", "h" } };
List<List<string>> csvTextAsList = Csv.FromStringToList(csvText);
bool ok = Enumerable.SequenceEqual(csvList[0], csvTextAsList[0]) && Enumerable.SequenceEqual(csvList[1], csvTextAsList[1]);
string csvListAsText = Csv.FromListToString(csvList);
return ok && csvListAsText == csvText;
}
}
Usage examples:
// get List<List<string>> representation of csv
var csvFromText = Csv.FromStringToList(csvAsText);
// read csv file with custom separator and quote
// return no header and ignore empty rows
var csvFile = File.ReadAllText(csvFileFullPath);
var csvFromFile = Csv.FromStringToList(csvFile, ";", '"', false, false);
// get text representation of csvData from List<List<string>>
var csvAsText = Csv.FromListToString(csvData);
Notes:
This: char tempQuote = (char)162; is first rare character from ASCI table. The script searches for this, or the first next few ascii character that is NOT in the text and uses it as a temporary escape and quote characters.
Still wrong. You need to compensate for "" in quotes.
Here is my solution Microsoft style csv.
/// <summary>
/// Microsoft style csv file. " is the quote character, "" is an escaped quote.
/// </summary>
/// <param name="fileName"></param>
/// <param name="sepChar"></param>
/// <param name="quoteChar"></param>
/// <param name="escChar"></param>
/// <returns></returns>
public static List<string[]> ReadCSVFileMSStyle(string fileName, char sepChar = ',', char quoteChar = '"')
{
List<string[]> ret = new List<string[]>();
string[] csvRows = System.IO.File.ReadAllLines(fileName);
foreach (string csvRow in csvRows)
{
bool inQuotes = false;
List<string> fields = new List<string>();
string field = "";
for (int i = 0; i < csvRow.Length; i++)
{
if (inQuotes)
{
// Is it a "" inside quoted area? (escaped litteral quote)
if(i < csvRow.Length - 1 && csvRow[i] == quoteChar && csvRow[i+1] == quoteChar)
{
i++;
field += quoteChar;
}
else if(csvRow[i] == quoteChar)
{
inQuotes = false;
}
else
{
field += csvRow[i];
}
}
else // Not in quoted region
{
if (csvRow[i] == quoteChar)
{
inQuotes = true;
}
if (csvRow[i] == sepChar)
{
fields.Add(field);
field = "";
}
else
{
field += csvRow[i];
}
}
}
if (!string.IsNullOrEmpty(field))
{
fields.Add(field);
field = "";
}
ret.Add(fields.ToArray());
}
return ret;
}
}
I have a library that is doing exactly you need.
Some time ago I had wrote simple and fast enough library for work with CSV files. You can find it by the following link: https://github.com/ukushu/DataExporter/blob/master/Csv.cs
It works with CSV like with 2 dimensions array. Exactly like you need.
As example, in case of you need all of values of 3rd row only you need is to write:
Csv csv = new Csv();
csv.FileOpen("c:\\file1.csv");
var allValuesOf3rdRow = csv.Rows[2];
or to read 2nd cell of 3rd row:
var value = csv.Rows[2][1];
Headers are required in csv for json conversion in the below code
You can use below code as is without making any changes.
This code will work with two row headers or with one row header.
Below code reads the uploaded IForm File and converts to memory stream.
If you want to use file path instead of uploaded file you can replace
new StreamReader(ms, System.Text.Encoding.UTF8, true)) with new StreamReader("../../examplefilepath");
using (var ms = new MemoryStream())
{
administrativesViewModel.csvFile.CopyTo(ms);
ms.Position = 0;
using (StreamReader csvReader = new StreamReader(ms, System.Text.Encoding.UTF8, true))
{
List<string> lines = new List<string>();
while (!csvReader.EndOfStream)
{
var line = csvReader.ReadLine();
var values = line.Split(';');
if (values[0] != "" && values[0] != null)
{
lines.Add(values[0]);
}
}
var csv = new List<string[]>();
foreach (string item in lines)
{
csv.Add(item.Split(','));
}
var properties = lines[0].Split(',');
int csvI = 1;
var listObjResult = new List<Dictionary<string, string>>();
if (lines.Count() > 1)
{
var ln = lines[0].Substring(0, lines[0].Count() - 1);
var ln1 = lines[1].Substring(0, lines[1].Count() - 1);
var lnSplit = ln.Split(',');
var ln1Split = ln1.Split(',');
if (lnSplit.Count() != ln1Split.Count())
{
properties = lines[1].Split(',');
csvI = 2;
}
}
for (int i = csvI; i < csv.Count(); i++)
{
var objResult = new Dictionary<string, string>();
if (csvI > 0)
{
var splitProp = lines[0].Split(":");
if (splitProp.Count() > 1)
{
if (splitProp[0] != "" && splitProp[0] != null && splitProp[1] != "" && splitProp[1] != null)
{
objResult.Add(splitProp[0], splitProp[1]);
}
}
}
for (int j = 0; j < properties.Length; j++)
if (!properties[j].Contains(":"))
{
objResult.Add(properties[j], csv[i][j]);
}
listObjResult.Add(objResult);
}
var result = JsonConvert.SerializeObject(listObjResult);
var result2 = JArray.Parse(result);
Console.WriteLine(result2);
}
}
look at this
using CsvFramework;
using System.Collections.Generic;
namespace CvsParser
{
public class Customer
{
public int Id { get; set; }
public string Name { get; set; }
public List<Order> Orders { get; set; }
}
public class Order
{
public int Id { get; set; }
public int CustomerId { get; set; }
public int Quantity { get; set; }
public int Amount { get; set; }
public List<OrderItem> OrderItems { get; set; }
}
public class Address
{
public int Id { get; set; }
public int CustomerId { get; set; }
public string Name { get; set; }
}
public class OrderItem
{
public int Id { get; set; }
public int OrderId { get; set; }
public string ProductName { get; set; }
}
class Program
{
static void Main(string[] args)
{
var customerLines = System.IO.File.ReadAllLines(#"Customers.csv");
var orderLines = System.IO.File.ReadAllLines(#"Orders.csv");
var orderItemLines = System.IO.File.ReadAllLines(#"OrderItemLines.csv");
CsvFactory.Register<Customer>(builder =>
{
builder.Add(a => a.Id).Type(typeof(int)).Index(0).IsKey(true);
builder.Add(a => a.Name).Type(typeof(string)).Index(1);
builder.AddNavigation(n => n.Orders).RelationKey<Order, int>(k => k.CustomerId);
}, false, ',', customerLines);
CsvFactory.Register<Order>(builder =>
{
builder.Add(a => a.Id).Type(typeof(int)).Index(0).IsKey(true);
builder.Add(a => a.CustomerId).Type(typeof(int)).Index(1);
builder.Add(a => a.Quantity).Type(typeof(int)).Index(2);
builder.Add(a => a.Amount).Type(typeof(int)).Index(3);
builder.AddNavigation(n => n.OrderItems).RelationKey<OrderItem, int>(k => k.OrderId);
}, true, ',', orderLines);
CsvFactory.Register<OrderItem>(builder =>
{
builder.Add(a => a.Id).Type(typeof(int)).Index(0).IsKey(true);
builder.Add(a => a.OrderId).Type(typeof(int)).Index(1);
builder.Add(a => a.ProductName).Type(typeof(string)).Index(2);
}, false, ',', orderItemLines);
var customers = CsvFactory.Parse<Customer>();
}
}
}

Categories

Resources