I try to create a csv file in c#. First I wrote a little piece of code to test. I saw on that post that the "," character is use to create different columns but it doesn't work for me.
I searched on different topic but all answer are something like String.Format("{0},{1}", first, second);
My code :
String first = "Test name";
String second = "Value";
String newLine = String.Format("{0},{1}", first, second);
csv.AppendLine(newLine);
for (int i = 0; i < 3; i++)
{
first = "Test_" + i;
second = i.ToString();
newLine = String.Format("{0},{1}", first, second);
csv.AppendLine(newLine);
}
System.IO.File.WriteAllText(path, csv.ToString());
This create rightly the lines but all in column 1
Assuming csv to be a StringBuilder, this should create the file
Test name,Value
Test_0,0
Test_1,1
Test_2,2
which is a valid CSV file with two columns and three rows (and headers).
Are you by any chance using a non-English locale (specifically, one that uses the comma as a decimal separator) and trying to load the CSV in Excel? In that case Excel assumes ; as the field separator instead of ,. You can split the data with custom settings if needed (I believe the feature is called “Text to fields”, or “Text to columns” on the Data tab in the ribbon).
Related
I have a CSV file, I am trying to replace 1st column details with 2nd column values using String.Replace in C#,This is working fine. But next when I try replacing 2nd column with 6th column values, it is affecting the 1st column values also..?
string[] lines = File.ReadAllLines(file);
for(int i=1;i<lines.Length;i++)
{
if (lines[i].Split(',')[1].Contains('.'))
{
lines[i] = lines[i].Replace(lines[i].Split(',')[0], lines[i].Split(',')[1]);
lines[i] = lines[i].Replace(lines[i].Split(',')[1], lines[i].Split(',')[6]);
}
}
File.WriteAllLines(file,lines);
There is a miss understanding of what lines[i].Replace does. If you click on it and press F12 or F1 you will see that it's actually String.Replace(String, String).
From the documentation: "Returns a new string in which all occurrences of a specified string in the current instance are replaced with another specified string."
In your code you want to move values from column 2 to column 1 etc. Not remplace the all occurence of the value of column 1.
for (int i = 1; i < lines.Length; i++)
{
// your if here.
var columns = lines[i].Split(',');
columns[0] = columns[1];
columns[1] = columns[5];
lines[i] = string.Join(",", columns);
}
In the following Live demo, I removed the if and emulate fileRead and Write with simple string [] and string.
I have two list every list have different data i want to add the lists data to csv file my problem how to write in the next column.
The two list is dl, xl i want to write xl in column and dl in another column
this my code
using (StreamWriter sw = File.CreateText("list.csv"))
{
for (int i = 0; i < dl.Count; i++)
{
// the two list have the same count
sw.WriteLine(dl[i]);
sw.WriteLine(xl[i]); // i want to write this in another column
}
}
You are not writing an Excel file. You are writing a CSV file which is compatible with Excel. CSV stands for "Comma separated values," meaning that you should use the comma as a column separator.
So... change this:
sw.WriteLine(dl[i]);
sw.WriteLine(xl[i]); // i want to write this in another column
To this:
var line = String.Format("{0},{1}", dl[i], xl[i]);
sw.WriteLine(line);
As already explained in other answer, CSV files are a special type of text file, where we have multiple records of data, one row per line. (Like a table row in RDBMS tables). Now, each line/row can have multiple data-part(s) or columns or data (like data column in a table row), separated by comma ','.
Basically a CSV file represents a table of data. It is not an Excel file, but can be open with MS Excel and edited easily.
So, a typical csv data file will look like
EmployeeId,Name,Dept
1,John,CSharp
2,Eric,Compiler
So, to make a new data-row, you just write the contents in a new line. And to mark the columns, you separate them with comma. As asked by op, if each value of dl and xl are to be created as columns in the csv file, they should be written in a line separated by comma. This can be achieved by changing the code inside for loop to
sw.WriteLine(dl[i] + "," + xl[i]);
Here + is used to concatenate three string values, but for more stings or even for this, it can be done in a cleaner way using
string.Format("{0},{1}", dl[i], xl[i]);
to those who are searching to write in two column just add ;
string.Format("{0};{1}", dl[i], xl[i]);
I found the easiest way to do it was:
using (StreamWriter sw = File.CreateText("newfile.csv"))
{
for (int i = 0; i < threshhld; i++)
{
//writing two columns right next to eachother
sw.WriteLine(dl[i] + "," + xl[i]);
}
}
I have converted an asp.net c# project to framework 3.5 using VS 2008. Purpose of app is to parse a text file containing many rows of like information then inserting the data into a database.
I didn't write original app but developer used substring() to fetch individual fields because they always begin at the same position.
My question is:
What is best way to find the index of substring in text file without having to manually count the position? Does someone have preferred method they use to find position of characters in a text file?
I would say IndexOf() / IndexOfAny() together with Substring(). Alternatively, regular expressions. It the file has an XML-like structure, this.
If the files are delimited eg with commas you can use string.Split
If data is: string[] text = { "1, apple", "2, orange", "3, lemon" };
private void button1_Click(object sender, EventArgs e)
{
string[] lines = this.textBoxIn.Lines;
List<Fruit> fields = new List<Fruit>();
foreach(string s in lines)
{
char[] delim = {','};
string[] fruitData = s.Split(delim);
Fruit f = new Fruit();
int tmpid = 0;
Int32.TryParse(fruitData[0], out tmpid);
f.id = tmpid;
f.name = fruitData[1];
fields.Add(f);
}
this.textBoxOut.Clear();
string text=string.Empty;
foreach(Fruit item in fields)
{
text += item.ToString() + " \n";
}
this.textBoxOut.Text = text;
}
}
The text file I'm reading does not contain delimiters - sometimes there spaces between fields and sometimes they run together. In either case, every line is formatted the same. When I asked the question I was looking at the file in notepad.
Question was: how do you find the position in a file so that position (a number) could be specified as the startIndex of my substring function?
Answer: I've found that opening the text file in notepad++ will display the column # and line count of any position where the curser is in the file and makes this job easier.
You can use indexOf() and then use Length() as the second substring parameter
substr = str.substring(str.IndexOf("."), str.Length - str.IndexOf("."));
Here is just an example of the data I need to format.
The first column is simple, the problem the second column.
What would be the best approach to format multiple data fields in one column?
How to parse this data?
Important*: The second column needs to contain multiple values, like in an example below
Name Details
Alex Age:25
Height:6
Hair:Brown
Eyes:Hazel
A csv should probably look like this:
Name,Age,Height,Hair,Eyes
Alex,25,6,Brown,Hazel
Each cell should be separated by exactly one comma from its neighbor.
You can reformat it as such by using a simple regex which replaces certain newline and non-newline whitespace with commas (you can easily find each block because it has values in both columns).
A CSV file is normally defined using commas as field separators and CR for a row separator. You are using CR within your second column, this will cause problems. You'll need to reformat your second column to use some other form of separator between multiple values. A common alternate separator is the | (pipe) character.
Your format would then look like:
Alex,Age:25|Height:6|Hair:Brown|Eyes:Hazel
In your parsing, you would first parse the comma separated fields (which would return two values), and then parse the second field as pipe separated.
This is an interesting one - it can be quite difficult to parse specific format files which is why people often write specific classes to deal with them. More conventional file formats like CSV, or other delimited formats are [more] easy to read because they are formatted in a similar way.
A problem like the above can be addressed in the following way:
1) What should the output look like?
In your instance, and this is just a guess, but I believe you are aiming for the following:
Name, Age, Height, Hair, Eyes
Alex, 25, 6, Brown, Hazel
In which case, you have to parse out this information based on the structure above. If it's repeated blocks of text like the above then we can say the following:
a. Every person is in a block starting with Name Details
b. The name value is the first text after Details, with the other columns being delimited in the format Column:Value
However, you might also have sections with addtional attributes, or attributes that are missing if the original input was optional, so tracking the column and ordinal would be useful too.
So one approach might look like the following:
public void ParseFile(){
String currentLine;
bool newSection = false;
//Store the column names and ordinal position here.
List<String> nameOrdinals = new List<String>();
nameOrdinals.Add("Name"); //IndexOf == 0
Dictionary<Int32, List<String>> nameValues = new Dictionary<Int32 ,List<string>>(); //Use this to store each person's details
Int32 rowNumber = 0;
using (TextReader reader = File.OpenText("D:\\temp\\test.txt"))
{
while ((currentLine = reader.ReadLine()) != null) //This will read the file one row at a time until there are no more rows to read
{
string[] lineSegments = currentLine.Split(new[] { " " }, StringSplitOptions.RemoveEmptyEntries);
if (lineSegments.Length == 2 && String.Compare(lineSegments[0], "Name", StringComparison.InvariantCultureIgnoreCase) == 0
&& String.Compare(lineSegments[1], "Details", StringComparison.InvariantCultureIgnoreCase) == 0) //Looking for a Name Details Line - Start of a new section
{
rowNumber++;
newSection = true;
continue;
}
if (newSection && lineSegments.Length > 1) //We can start adding a new person's details - we know that
{
nameValues.Add(rowNumber, new List<String>());
nameValues[rowNumber].Insert(nameOrdinals.IndexOf("Name"), lineSegments[0]);
//Get the first column:value item
ParseColonSeparatedItem(lineSegments[1], nameOrdinals, nameValues, rowNumber);
newSection = false;
continue;
}
if (lineSegments.Length > 0 && lineSegments[0] != String.Empty) //Ignore empty lines
{
ParseColonSeparatedItem(lineSegments[0], nameOrdinals, nameValues, rowNumber);
}
}
}
//At this point we should have collected a big list of items. We can then write out the CSV. We can use a StringBuilder for now, although your requirements will
//be dependent upon how big the source files are.
//Write out the columns
StringBuilder builder = new StringBuilder();
for (int i = 0; i < nameOrdinals.Count; i++)
{
if(i == nameOrdinals.Count - 1)
{
builder.Append(nameOrdinals[i]);
}
else
{
builder.AppendFormat("{0},", nameOrdinals[i]);
}
}
builder.Append(Environment.NewLine);
foreach (int key in nameValues.Keys)
{
List<String> values = nameValues[key];
for (int i = 0; i < values.Count; i++)
{
if (i == values.Count - 1)
{
builder.Append(values[i]);
}
else
{
builder.AppendFormat("{0},", values[i]);
}
}
builder.Append(Environment.NewLine);
}
//At this point you now have a StringBuilder containing the CSV data you can write to a file or similar
}
private void ParseColonSeparatedItem(string textToSeparate, List<String> columns, Dictionary<Int32, List<String>> outputStorage, int outputKey)
{
if (String.IsNullOrWhiteSpace(textToSeparate)) { return; }
string[] colVals = textToSeparate.Split(new[] { ":" }, StringSplitOptions.RemoveEmptyEntries);
List<String> outputValues = outputStorage[outputKey];
if (!columns.Contains(colVals[0]))
{
//Add the column to the list of expected columns. The index of the column determines it's index in the output
columns.Add(colVals[0]);
}
if (outputValues.Count < columns.Count)
{
outputValues.Add(colVals[1]);
}
else
{
outputStorage[outputKey].Insert(columns.IndexOf(colVals[0]), colVals[1]); //We append the value to the list at the place where the column index expects it to be. That way we can miss values in certain sections yet still have the expected output
}
}
After running this against your file, the string builder contains:
"Name,Age,Height,Hair,Eyes\r\nAlex,25,6,Brown,Hazel\r\n"
Which matches the above (\r\n is effectively the Windows new line marker)
This approach demonstrates how a custom parser might work - it's purposefully over verbose as there is plenty of refactoring that could take place here, and is just an example.
Improvements would include:
1) This function assumes there are no spaces in the actual text items themselves. This is a pretty big assumption and, if wrong, would require a different approach to parsing out the line segments. However, this only needs to change in one place - as you read a line at a time, you could apply a reg ex, or just read in characters and assume that everything after the first "column:" section is a value, for example.
2) No exception handling
3) Text output is not quoted. You could test each value to see if it's a date or number - if not, wrap it in quotes as then other programs (like Excel) will attempt to preserve the underlying datatypes more effectively.
4) Assumes no column names are repeated. If they are, then you have to check if a column item has already been added, and then create an ColName2 column in the parsing section.
I've got a CSV string an I want to separate it into an array. However the CSV is a mix of strings and numbers where the strings are enclosed in quotes and may contain commas.
For example, I might have a CSV as follows:
1,"Hello",2,"World",3,"Hello, World"
I would like it so the string is split into:
1
"Hello"
2
"World"
3
"Hello, World"
If I use String.Split(','); I get:
1
"Hello"
2
"World"
3
"Hello
World"
Is there an easy way of doing this? A library that is already written or do I have to parse the string character by character?
The "A Fast CSV Reader" article on Code Project. I've used it happily many times.
String.Split() is icky for this. Not only does it have nasty corner cases where it doesn't work like the one you just found (and others you haven't seen yet), but performance is less than ideal as well. The FastCSVReader posted by others will work, there's a decent csv parser built into the framework (Microsoft.VisualBasic.TextFieldParser), and I have a simple parser that behaves correctly posted to this question.
I would suggest using one of the following solutions, was just testing a few of them (hence the delay):-
Regex matching commas not found within an enclosing double aprostophe
A Fast CSV Reader - for read CSV only
FileHelpers Library 2.0 - for read/write CSV
Hope this helps.
It's not the most elegant solution, but the quickest if you want to just quickly copy and paste code (avoiding having to import DLLs or other code libraries):
private string[] splitQuoted(string line, char delimeter)
{
string[] array;
List<string> list = new List<string>();
do
{
if (line.StartsWith("\""))
{
line = line.Substring(1);
int idx = line.IndexOf("\"");
while (line.IndexOf("\"", idx) == line.IndexOf("\"\"", idx))
{
idx = line.IndexOf("\"\"", idx) + 2;
}
idx = line.IndexOf("\"", idx);
list.Add(line.Substring(0, idx));
line = line.Substring(idx + 2);
}
else
{
list.Add(line.Substring(0, Math.Max(line.IndexOf(delimeter), 0)));
line = line.Substring(line.IndexOf(delimeter) + 1);
}
}
while (line.IndexOf(delimeter) != -1);
list.Add(line);
array = new string[list.Count];
list.CopyTo(array);
return array;
}