I have 5 listviews that I want to export it out into one CSV file.
This is my 5 listview
In the listview, you can see that the Date Time is the same for all 6 listviews.
So I was able to export the first listview into a CSV File.
saveFileDialog1.Filter = "csv files (*.csv)|*.csv";
saveFileDialog1.FileName = "logs";
saveFileDialog1.Title = "Export to Excel";
StringBuilder sb = new StringBuilder();
foreach (ColumnHeader ch in listView1.Columns)
{
sb.Append(ch.Text + ",");
}
sb.AppendLine();
foreach (ListViewItem lvi in listView1.Items)
{
foreach (ListViewItem.ListViewSubItem lvs in lvi.SubItems)
{
if (lvs.Text.Trim() == string.Empty)
sb.Append(" ,");
else
sb.Append(lvs.Text + ",");
}
sb.AppendLine();
}
DialogResult dr = saveFileDialog1.ShowDialog();
if (dr == DialogResult.OK)
{
StreamWriter sw = new StreamWriter(saveFileDialog1.FileName);
sw.Write(sb.ToString());
sw.Close();
this.Close();
}
Now I want to export the remaining other 4 listview into the same CSV file where the first colomn will be the Date Time which is the same for all 5 listview.
How do I go about doing so?
Complete code to accomplish your task
class ListViewToCSV
{
public static void ListViewToCSV(ListView listView, string filePath, bool includeHidden)
{
//make header string
StringBuilder result = new StringBuilder();
WriteCSVRow(result, listView.Columns.Count, i => includeHidden || listView.Columns[i].Width > 0, i => listView.Columns[i].Text);
//export data rows
foreach (ListViewItem listItem in listView.Items)
WriteCSVRow(result, listView.Columns.Count, i => includeHidden || listView.Columns[i].Width > 0, i => listItem.SubItems[i].Text);
File.WriteAllText(filePath, result.ToString());
}
private static void WriteCSVRow(StringBuilder result, int itemsCount, Func<int, bool> isColumnNeeded, Func<int, string> columnValue)
{
bool isFirstTime = true;
for (int i = 0; i < itemsCount; i++)
{
if (!isColumnNeeded(i))
continue;
if (!isFirstTime)
result.Append(",");
isFirstTime = false;
result.Append(String.Format("\"{0}\"", columnValue(i)));
}
result.AppendLine();
}
}
Related
I have for like 40 rows on My DataTable Displayed in a DataGridView
i'm confused why my method Saves Only One Row in the TextFile :
private void SaveBtn_Click(object sender, EventArgs e)
{
String outputFile;
List<String> ListData = new List<String>();
using (SaveFileDialog sfd = new SaveFileDialog())
{
sfd.Filter = "Txt File|*.Txt";
if (sfd.ShowDialog() != DialogResult.OK)
return;
outputFile = sfd.FileName;
}
DataTable tb = pw.SavedInfo(User_info.UserID);
for (int i = 0; i < tb.Rows.Count; i++)
{
ListData.Add("Name==> " + tb.Rows[i][1].ToString() + " LastName ==> " + tb.Rows[i][2].ToString() + " Email ==> " + tb.Rows[i][3].ToString() );
}
foreach (String s in ListData)
{
using (TextWriter Tw = new StreamWriter(outputFile))
{
Tw.WriteLine(s);
}
}
}
Did i missed something ? cause it was a really long day to keep being focused
Use the same StreamWriter:
using (TextWriter Tw = new StreamWriter(outputFile))
{
foreach (String s in ListData)
{
Tw.WriteLine(s);
}
}
or use the constructor that takes a bool for "append":
foreach (String s in ListData)
{
using (TextWriter Tw = new StreamWriter(outputFile, true))
{
Tw.WriteLine(s);
}
}
File.WriteAllLines(outputFile, lisData);
Use this to write in the file. File.WriteAllLines Documentation
I have a problem when I want to delete a row in a CSV File, I have this code but only deletes the field that contains the line.
Example:
CSV File:
ID,Name,Lastname,Country
1,David,tod,UK
2,Juan,Perez,Germ
3,Pepe,Lopez,Col
First iteration, sending the id 1 to delete the line:
ID,Name,Lastname,Country
David,tod,UK
2,Juan,Perez,Germ
3,Pepe,Lopez,Arg
Just delete the id I want, but not the whole line
The expected result would be that like this:
ID,Name,Lastname,Country
2,Juan,Perez,Arg
3,Pepe,Lopez,Col
this is my code, What am I doing wrong? I have never used csv in C# :(
string searchid = "1";
string[] values = File.ReadAllText("C:\\registros.csv").Split(new char[] { ',' });
StringBuilder ObjStringBuilder = new StringBuilder();
for (int i = 0; i < values.Length; i++)
{
if (values[i].Contains(searchid))
continue;
ObjStringBuilder.Append(values[i] + ",");
}
ObjStringBuilder.ToString().Remove(ObjStringBuilder.Length - 1);
File.WriteAllText("\\registros.csv", ObjStringBuilder.ToString());
Another question is how can I show the CSV file in a datagridview in Windows Forms. I have this logic, don't know if this is correct, but how I can show it?
public DataTable ConvertCSVtoDataTable()
{
StreamReader sr = new StreamReader("\\registros.csv");
string[] headers = sr.ReadLine().Split(',');
DataTable dt = new DataTable();
foreach (string header in headers)
{
dt.Columns.Add(header);
}
while (!sr.EndOfStream)
{
string[] rows = Regex.Split(sr.ReadLine(), ",(?=(?:[^\"]*\"[^\"]*\")*[^\"]*$)");
DataRow dr = dt.NewRow();
for (int i = 0; i < headers.Length; i++)
{
dr[i] = rows[i];
}
dt.Rows.Add(dr);
}
return dt;
}
Thanks!
You can delete row from CSV using below link
Delete rows from CSV
and
You can convert the CSV into DataTable using the below code. If your csv file uses delimiter as ,
public DataTable ReadCSV(String FilePath, Boolean IsHeader)
{
string strConn = null;
string folderpath = null;
try
{
folderpath = FilePath.Substring(0, FilePath.LastIndexOf("\\") + 1);
string FileName = Path.GetFileName(FilePath);
if (IsHeader == true)
{
strConn = "Provider=Microsoft.Jet.OLEDB.4.0;" + "Data Source=" + folderpath + ";" + "Extended Properties=\"text;HDR=YES\"";
}
else
{
strConn = "Provider=Microsoft.Jet.OLEDB.4.0;" + "Data Source=" + folderpath + ";" + "Extended Properties=\"text;HDR=NO\"";
}
OleDbConnection Conn = new OleDbConnection();
Conn.ConnectionString = strConn;
Conn.Open();
string s1 = "select * from [" + FileName + "]";
OleDbDataAdapter da1 = new OleDbDataAdapter(s1, Conn);
DataSet dtall = new DataSet();
da1.Fill(dtall);
Conn.Close();
return dtall.Tables[0].Copy();
}
catch (Exception ex)
{
Exception excep = new Exception("CSV : " + ex.Message);
throw excep;
}
}
Reading and writing CSV files is not as trivial as it first seems. Cells can have embedded commas, and even new line characters. The following is one implementation of a CSV reader which can optionally be run asynchronously as a background worker. This implementation returns a standard DataTable which can easily be bound to a DataGridView:
grid.DataSource = dataTable;
The CsvReader class:
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.IO;
using System.Linq;
using System.Text;
namespace CsvReaderExample
{
public class CsvReader
: BackgroundWorker
{
string[] m_lines;
public DataTable DataTable { get; private set; }
public CsvReader(string[] lines)
{
m_lines = lines;
WorkerReportsProgress = true;
WorkerSupportsCancellation = true;
}
public DataTable RunWorker()
{
return DataTable = ParseCsvLines();
}
protected override void OnDoWork(DoWorkEventArgs e)
{
base.OnDoWork(e);
e.Result = DataTable = ParseCsvLines();
}
private DataTable ParseCsvLines()
{
if (m_lines.Length == 0)
return null;
var table = new DataTable();
var columns = table.Columns;
var columnNames = GetRowValues(m_lines[0]);
foreach (var columnName in columnNames)
{
var name = columnName;
int number = 2;
while (columns.Contains(name))
name += " " + number++;
columns.Add(name);
}
var rows = table.Rows;
for (int index = 1, linesCount = m_lines.Length; index < linesCount; index++)
{
if (CancellationPending)
return null;
var line = m_lines[index];
var values = GetRowValues(line);
int valueCount = values.Count;
if (valueCount > columns.Count)
{
int columnNumber = columns.Count;
while (columns.Contains(columnNumber.ToString()))
columnNumber++;
columns.Add(columnNumber.ToString());
}
rows.Add(values.ToArray());
if (WorkerReportsProgress)
ReportProgress(100 * index / linesCount);
}
return table;
}
const char COMMA = ',',
DOUBLE_QUOTE = '"',
VERTICAL_BAR = '|';
private List<string> GetRowValues(string line)
{
var builder = new StringBuilder();
var values = new List<string>();
var inDoubleQuotes = false;
var maxIndex = line.Length - 1;
for (int index = 0; index <= maxIndex; index++)
{
char c = line[index];
if (c == DOUBLE_QUOTE)
{
if (index == 0)
{
inDoubleQuotes = true;
continue;
}
if (index < maxIndex)
{
var nextIndex = index + 1;
if (nextIndex < maxIndex)
{
if (line[nextIndex] == DOUBLE_QUOTE)
{
index++;
if (inDoubleQuotes)
builder.Append(DOUBLE_QUOTE);
continue;
}
}
}
inDoubleQuotes = !inDoubleQuotes;
continue;
}
if (c == COMMA)
{
if (inDoubleQuotes)
{
builder.Append(c);
continue;
}
values.Add(builder.ToString());
builder = new StringBuilder();
continue;
}
builder.Append(c);
}
values.Add(builder.ToString());
return values;
}
#region Sanitise cells with new line characters
public static void SanitiseCellsWithNewLineCharacters(string fileName)
{
var text = File.ReadAllText(fileName, Encoding.Default);
text = text.Replace("\r\n", "\n");
text = text.Replace("\r", "\n");
using (var writer = File.CreateText(fileName))
{
var inDoubleQuotes = false;
foreach (char c in text)
{
if (c == '\n' && inDoubleQuotes)
{
writer.Write(VERTICAL_BAR);
continue;
}
if (c == DOUBLE_QUOTE)
{
if (inDoubleQuotes)
inDoubleQuotes = false;
else
inDoubleQuotes = true;
}
writer.Write(c);
}
}
}
#endregion
}
}
You can read the DataTable synchronously as follows:
var lines = File.ReadAllLines("C:\\registros.csv");
var csvReader = new CsvReader(lines);
var dataTable = csvReader.RunWorker();
You could then remove row(s) from the DataTable with a method such as:
private static void RemoveById(DataTable dataTable, int id)
{
var column = dataTable.Columns["ID"];
if (column == null)
return;
var rows = dataTable.Rows;
for (int index = rows.Count - 1; index >= 0; index--)
{
var row = rows[index];
var value = row ["ID"];
if (value == null)
continue;
if (value.Equals(id))
{
rows.RemoveAt(index);
return;
}
}
}
Call it:
RemoveById(dataTable, 1);
The first thing that is wrong with your implementation is that you use ',' as the separator. You should either split on the new-line character '\n' or read the file line by line as follows:
var lines = new List<string>();
var file = new System.IO.StreamReader("c:\\registros.csv");
string line;
while((line = file.ReadLine()) != null)
{
lines.Add(line);
}
file.Close();
You could then look for the line that starts with the id you are looking for. When you find it, remove the line from the list.
for(int i=0; i++; i<lines.Count)
{
if (lines[i].StartsWith(searchid))
{
lines.RemoveAt(i);
break;
}
}
Next step is to write the result back to the file:
File.WriteAllLines("c:\\registros.csv", lines);
Regarding your second question, I found a similar Q/A on stackoverflow here.
First step is creating the DataTable, then you'll have to bind the table to the table control that will show the data.
SIMPLE & UNDERSTANDABLE!`
Solution For your First Problem is:
****Reading & Writing back to CSV File!****
string searchid = "1";
string[] values = File.ReadAllText(#"Complete Path Of File").Split(new char[] { '\n' });
StringBuilder ObjStringBuilder = new StringBuilder();
for (int i = 0; i < values.Length - 1; i++)
{
if (values[i].StartsWith(searchid) == false)
{
ObjStringBuilder.Append(values[i]+"\n");
}
}
File.WriteAllText(#"Complete Path Of File", ObjStringBuilder.ToString());
}
Answer to your Second Doubt:
****Populating DataGridView dynamically from CSV File!****
Comma(,) Problem SOLVED:
DataTable dtDataSource = new DataTable();
string[] fileContent = File.ReadAllLines(#"..\\Book1.csv");
if (fileContent.Count() > 0)
{
//Create data table columns dynamically
string[] columns = fileContent[0].Split(',');
for (int i = 0; i < columns.Count(); i++)
{
dtDataSource.Columns.Add(columns[i]);
}
//Add row data dynamically
for (int i = 1; i < fileContent.Count(); i++)
{
string[] rowData = fileContent[i].Split(',');
string[] realRowData = new string[columns.Count()];
StringBuilder collaboration = new StringBuilder();
int v = 0;
//this region solves the problem of a cell containing ",".
#region CommaSepProblem
for (int j = 0, K = 0; j < rowData.Count(); j++, K++)
{
//After splitting the line with commas. The cells containing commas will also be splitted.
//Fact: if a cell contains special symbol in excel that cell will be saved in .csv contained in quotes E.g A+B will be saved "A+B" or A,B will be saved as "A,B"
//Our code splits everything where comma is found. So solution is:
//Logic: After splitting if a string contains even number of DoubleQuote then its perfect cell otherwise, it is splitted in multiple cells of array.
if ((rowData[j].Count(x => x == '"') % 2 == 0))//checks if the string contains even number of DoubleQuotes
{
realRowData[K] = quotesLogic((rowData[j]));
}
else if ((rowData[j].Count(x => x == '"') % 2 != 0))//If Number of DoubleQuotes are ODD
{
int c = rowData[j].Count(x => x == '"');
v = j;
while (c % 2 != 0)//Go through all the next array cell till it makes EVEN Number of DoubleQuotes.
{
collaboration.Append(rowData[j] + ",");
j++;
c += rowData[j].Count(x => x == '"');
}
collaboration.Append(rowData[j]);
realRowData[K] = quotesLogic(collaboration.ToString());
}
else { continue; }
}
#endregion
dtDataSource.Rows.Add(realRowData);
}
if (dtDataSource != null)
{
dataGrid1.ItemsSource = dtDataSource.DefaultView;
}
}
Add This Method Too:
string quotesLogic(string collaboration)
{
StringBuilder after = new StringBuilder(collaboration);
if (after.ToString().StartsWith("\"") && after.ToString().EndsWith("\""))//removes 1st and last quotes as those are system generated
{
after.Remove(0, 1);
after.Remove(after.Length - 1, 1);
int count = after.Length - 1;
//FACT: if you try to add DoubleQuote in a cell in excel. It'll save that quote as 2 times DoubleQuote(Like "") which means first DoubleQuote is to give instruction to CPU that the next DoubleQuote is not system generated.
while (count > 0)//This loop find twice insertion of 2 DoubleQuotes and neutralise them to One DoubleQuote.
{
if (after[count] == '"' && after[count - 1] == '"')
{
after.Remove(count, 1);
}
count--;
}
}
return after.ToString();
}
I am trying to create a program that will replace a string inside .txt file.
heres the trick. I am replacing the string in the file if they are checked,
but when I do an alternate check its still replacing the other.
private void BatchReplace()
{
string sourceFolder = FilePath.Text;
string searchWord = Searchbar.Text;
DateTime now = DateTime.Now;
List<string> allFiles = new List<string>();
AddFileNamesToList(sourceFolder, allFiles);
if (listView1.CheckedItems.Count != 0)
{
foreach (String file in allFiles)
{
for (int x = 0; x <= listView1.CheckedItems.Count - 1; x++)
{
if (file.Contains(listView1.CheckedItems[x].Text))
{
MessageBox.Show("File contains: " + listView1.CheckedItems[x].Text);
try
{
DialogResult dialogResult = MessageBox.Show("Are you sure you want to replace \"" + Searchbar.Text + "\" with \"" + Replacebar.Text + "\"?", "WARNING!", MessageBoxButtons.YesNo);
if (dialogResult == DialogResult.Yes)
{
StreamReader reader = new StreamReader(file);
string content = reader.ReadToEnd();
reader.Close();
content = Regex.Replace(content, Searchbar.Text, Replacebar.Text);
StreamWriter writer = new StreamWriter(file);
writer.Write(content); writer.Close();
}
else
{
}
}
catch
{
}
}
}
}
}
}
else
{
MessageBox.Show("Please Check the files you want to rename");
}
}
public static void AddFileNamesToList(string sourceDir, List<string> allFiles)
{
string[] fileEntries = Directory.GetFiles(sourceDir);
try
{
foreach (string fileName in fileEntries)
{
allFiles.Add(fileName);
}
//Recursion
string[] subdirectoryEntries = Directory.GetDirectories(sourceDir);
foreach (string item in subdirectoryEntries)
{
// Avoid "reparse points"
if ((File.GetAttributes(item) & FileAttributes.ReparsePoint) != FileAttributes.ReparsePoint)
{
AddFileNamesToList(item, allFiles);
}
}
}
I am still confused about what you are trying to do, but to simplify things, why don't you, when you populate the ListView with the files in the directory, add the file path (or a file object) to the tag property of the ListViewitem?
That way, when you loop through the checked items, you can just retrieve the file directly instead of having to loop through two Lists at once.
Something like:
private void BatchReplace()
{
string sourceFolder = FilePath.Text;
string searchWord = Searchbar.Text;
AddFileNamesToList(sourceFolder);
if (listView1.CheckedItems.Count == 0)
{
MessageBox.Show("Please Check the files you want to rename");
return;
}
for (int x = 0; x < listView1.CheckedItems.Count; x++)
{
var file = listView1.CheckedItems[x].Tag.ToString()
try
{
DialogResult dialogResult = MessageBox.Show("Are you sure you want to replace \"" + Searchbar.Text + "\" with \"" + Replacebar.Text + "\"?", "WARNING!", MessageBoxButtons.YesNo);
if (dialogResult == DialogResult.Yes)
{
StreamReader reader = new StreamReader(file);
string content = reader.ReadToEnd();
reader.Close();
content = Regex.Replace(content, SearchWord, Replacebar.Text);
StreamWriter writer = new StreamWriter(file);
writer.Write(content);
writer.Close();
}
}
catch
{
}
}
}
}
Sorry for the indenting and also if this doesn't work straight as is, I haven't tested this.
How to save the ListView contents (including the ColumnHeaders) to a text file?
thanks.
There is nothing in .NET that will do this for you, you need to do the work yourself.
On whatever event will trigger your save: open the file, iterate through the list content writing the text to the file and then close the file. The close can of course be done via using:
using (var tw = new StreamWriter(filename)) {
foreach (ListViewItem item in listView.Items) {
tw.WriteLine(item.Text);
}
}
If you want to export all the subitems you must use this code:
StringBuilder sb;
if (listView.Items.Count > 0)
{
// the actual data
foreach (ListViewItem lvi in listView.Items)
{
sb = new StringBuilder();
foreach (ListViewItem.ListViewSubItem listViewSubItem in lvi.SubItems)
{
sb.Append(string.Format("{0}\t", listViewSubItem.Text));
}
sw.WriteLine(sb.ToString());
}
sw.WriteLine();
}
This should work 100%, I made this for a project of mine.
I know this is 4 years too late but here it is.
private void export2File(ListView lv, string splitter)
{
string filename = "";
SaveFileDialog sfd = new SaveFileDialog();
sfd.Title = "SaveFileDialog Export2File";
sfd.Filter = "Text File (.txt) | *.txt";
if (sfd.ShowDialog() == DialogResult.OK)
{
filename = sfd.FileName.ToString();
if (filename != "")
{
using (StreamWriter sw = new StreamWriter(filename))
{
foreach (ListViewItem item in lv.Items)
{
sw.WriteLine("{0}{1}{2}", item.SubItems[0].Text, splitter, item.SubItems[1].Text);
}
}
}
}
}
Currently have a DataTable, but wish to stream it to the user via a WebHandler. FileHelpers has CommonEngine.DataTableToCsv(dt, "file.csv"). However it saves it to a file. How can I save it to a stream instead? I know how to do it when I know the columns in advanced or they don't change, but I want to generate the column headings straight from the data table.
If I know the columns I just create the class:
[DelimitedRecord(",")]
public class MailMergeFields
{
[FieldQuoted()]
public string FirstName;
[FieldQuoted()]
public string LastName;
}
Then use FileHelperEngine and add the records:
FileHelperEngine engine = new FileHelperEngine(typeof(MailMergeFields));
MailMergeFields[] merge = new MailMergeFields[dt.Rows.Count + 1];
// add headers
merge[0] = new MailMergeFields();
merge[0].FirstName = "FirstName";
merge[0].LastName = "LastName";
int i = 1;
// add records
foreach (DataRow dr in dt.Rows)
{
merge[i] = new MailMergeFields();
merge[i].FirstName = dr["Forename"];
merge[i].LastName = dr["Surname"];
i++;
}
Finally write to a stream:
TextWriter writer = new StringWriter();
engine.WriteStream(writer, merge);
context.Response.Write(writer.ToString());
Unfortunately as I don't know the columns before hand, I can't create the class before hand.
You can just write something quickly yourself:
public static class Extensions
{
public static string ToCSV(this DataTable table)
{
var result = new StringBuilder();
for (int i = 0; i < table.Columns.Count; i++)
{
result.Append(table.Columns[i].ColumnName);
result.Append(i == table.Columns.Count - 1 ? "\n" : ",");
}
foreach (DataRow row in table.Rows)
{
for (int i = 0; i < table.Columns.Count; i++)
{
result.Append(row[i].ToString());
result.Append(i == table.Columns.Count - 1 ? "\n" : ",");
}
}
return result.ToString();
}
}
And to test:
public static void Main()
{
DataTable table = new DataTable();
table.Columns.Add("Name");
table.Columns.Add("Age");
table.Rows.Add("John Doe", "45");
table.Rows.Add("Jane Doe", "35");
table.Rows.Add("Jack Doe", "27");
var bytes = Encoding.GetEncoding("iso-8859-1").GetBytes(table.ToCSV());
MemoryStream stream = new MemoryStream(bytes);
StreamReader reader = new StreamReader(stream);
Console.WriteLine(reader.ReadToEnd());
}
EDIT: Re your comments:
It depends on how you want your csv formatted but generally if the text contains special characters, you want to enclose it in double quotes ie: "my,text". You can add checking in the code that creates the csv to check for special characters and encloses the text in double quotes if it is. As for the .NET 2.0 thing, just create it as a helper method in your class or remove the word this in the method declaration and call it like so : Extensions.ToCsv(table);
Update 1
I have modified it to use StreamWriter instead, add an option to check if you need column headers in your output.
public static bool DataTableToCSV(DataTable dtSource, StreamWriter writer, bool includeHeader)
{
if (dtSource == null || writer == null) return false;
if (includeHeader)
{
string[] columnNames = dtSource.Columns.Cast<DataColumn>().Select(column => "\"" + column.ColumnName.Replace("\"", "\"\"") + "\"").ToArray<string>();
writer.WriteLine(String.Join(",", columnNames));
writer.Flush();
}
foreach (DataRow row in dtSource.Rows)
{
string[] fields = row.ItemArray.Select(field => "\"" + field.ToString().Replace("\"", "\"\"") + "\"").ToArray<string>();
writer.WriteLine(String.Join(",", fields));
writer.Flush();
}
return true;
}
As you can see, you can choose the output by initial StreamWriter, if you use
StreamWriter(Stream BaseStream), you can write csv into MemeryStream, FileStream, etc.
Origin
I have an easy datatable to csv function, it serves me well:
public static void DataTableToCsv(DataTable dt, string csvFile)
{
StringBuilder sb = new StringBuilder();
var columnNames = dt.Columns.Cast<DataColumn>().Select(column => "\"" + column.ColumnName.Replace("\"", "\"\"") + "\"").ToArray();
sb.AppendLine(string.Join(",", columnNames));
foreach (DataRow row in dt.Rows)
{
var fields = row.ItemArray.Select(field => "\"" + field.ToString().Replace("\"", "\"\"") + "\"").ToArray();
sb.AppendLine(string.Join(",", fields));
}
File.WriteAllText(csvFile, sb.ToString(), Encoding.Default);
}
If you can turn your datatable into an IEnumerable this should work for you...
Response.Clear();
Response.Buffer = true;
Response.AddHeader("content-disposition", "attachment;filename=FileName.csv");
Response.Charset = "";
Response.ContentType = "application/text";
Response.Output.Write(ExampleClass.ConvertToCSV(GetListOfObject(), typeof(object)));
Response.Flush();
Response.End();
public static string ConvertToCSV(IEnumerable col, Type type)
{
StringBuilder sb = new StringBuilder();
StringBuilder header = new StringBuilder();
// Gets all properies of the class
PropertyInfo[] pi = type.GetProperties();
// Create CSV header using the classes properties
foreach (PropertyInfo p in pi)
{
header.Append(p.Name + ",");
}
sb.AppendLine(header.ToString().Remove(header.Length));
foreach (object t in col)
{
StringBuilder body = new StringBuilder();
// Create new item
foreach (PropertyInfo p in pi)
{
object o = p.GetValue(t, null);
body.Append(o.ToString() + ",");
}
sb.AppendLine(body.ToString().Remove(body.Length));
}
return sb.ToString();
}
I don't know if this converted from VB to C# ok but if you don't want quotes around your numbers, you might compare the data type as follows..
public string DataTableToCSV(DataTable dt)
{
StringBuilder sb = new StringBuilder();
if (dt == null)
return "";
try {
// Create the header row
for (int i = 0; i <= dt.Columns.Count - 1; i++) {
// Append column name in quotes
sb.Append("\"" + dt.Columns[i].ColumnName + "\"");
// Add carriage return and linefeed if last column, else add comma
sb.Append(i == dt.Columns.Count - 1 ? "\n" : ",");
}
foreach (DataRow row in dt.Rows) {
for (int i = 0; i <= dt.Columns.Count - 1; i++) {
// Append value in quotes
//sb.Append("""" & row.Item(i) & """")
// OR only quote items that that are equivilant to strings
sb.Append(object.ReferenceEquals(dt.Columns[i].DataType, typeof(string)) || object.ReferenceEquals(dt.Columns[i].DataType, typeof(char)) ? "\"" + row[i] + "\"" : row[i]);
// Append CR+LF if last field, else add Comma
sb.Append(i == dt.Columns.Count - 1 ? "\n" : ",");
}
}
return sb.ToString;
} catch (Exception ex) {
// Handle the exception however you want
return "";
}
}
If you wish to stream the CSV out to the user without creating a file then I found the following to be the simplest method. You can use any extension/method to create the ToCsv() function (which returns a string based on the given DataTable).
var report = myDataTable.ToCsv();
var bytes = Encoding.GetEncoding("iso-8859-1").GetBytes(report);
Response.Buffer = true;
Response.Clear();
Response.AddHeader("content-disposition", "attachment; filename=report.csv");
Response.ContentType = "text/csv";
Response.BinaryWrite(bytes);
Response.End();
I've used the following code, pillaged from someone's blog (pls forgive lack of citation). It takes care of quotations, newline and comma in a reasonably elegant way by quoting out each field value.
/// <summary>
/// Converts the passed in data table to a CSV-style string.
/// </summary>
/// <param name="table">Table to convert</param>
/// <returns>Resulting CSV-style string</returns>
public static string ToCSV(this DataTable table)
{
return ToCSV(table, ",", true);
}
/// <summary>
/// Converts the passed in data table to a CSV-style string.
/// </summary>
/// <param name="table">Table to convert</param>
/// <param name="includeHeader">true - include headers<br/>
/// false - do not include header column</param>
/// <returns>Resulting CSV-style string</returns>
public static string ToCSV(this DataTable table, bool includeHeader)
{
return ToCSV(table, ",", includeHeader);
}
/// <summary>
/// Converts the passed in data table to a CSV-style string.
/// </summary>
/// <param name="table">Table to convert</param>
/// <param name="includeHeader">true - include headers<br/>
/// false - do not include header column</param>
/// <returns>Resulting CSV-style string</returns>
public static string ToCSV(this DataTable table, string delimiter, bool includeHeader)
{
var result = new StringBuilder();
if (includeHeader)
{
foreach (DataColumn column in table.Columns)
{
result.Append(column.ColumnName);
result.Append(delimiter);
}
result.Remove(--result.Length, 0);
result.Append(Environment.NewLine);
}
foreach (DataRow row in table.Rows)
{
foreach (object item in row.ItemArray)
{
if (item is DBNull)
result.Append(delimiter);
else
{
string itemAsString = item.ToString();
// Double up all embedded double quotes
itemAsString = itemAsString.Replace("\"", "\"\"");
// To keep things simple, always delimit with double-quotes
// so we don't have to determine in which cases they're necessary
// and which cases they're not.
itemAsString = "\"" + itemAsString + "\"";
result.Append(itemAsString + delimiter);
}
}
result.Remove(--result.Length, 0);
result.Append(Environment.NewLine);
}
return result.ToString();
}
You can try using something like this.
In this case I used one stored procedure to get more data tables and export all of them
using CSV.
using System;
using System.Text;
using System.Data;
using System.Data.SqlClient;
using System.IO;
namespace bo
{
class Program
{
static private void CreateCSVFile(DataTable dt, string strFilePath)
{
#region Export Grid to CSV
// Create the CSV file to which grid data will be exported.
StreamWriter sw = new StreamWriter(strFilePath, false);
int iColCount = dt.Columns.Count;
// First we will write the headers.
//DataTable dt = m_dsProducts.Tables[0];
for (int i = 0; i < iColCount; i++)
{
sw.Write(dt.Columns[i]);
if (i < iColCount - 1)
{
sw.Write(";");
}
}
sw.Write(sw.NewLine);
// Now write all the rows.
foreach (DataRow dr in dt.Rows)
{
for (int i = 0; i < iColCount; i++)
{
if (!Convert.IsDBNull(dr[i]))
{
sw.Write(dr[i].ToString());
}
if (i < iColCount -1 )
{
sw.Write(";");
}
}
sw.Write(sw.NewLine);
}
sw.Close();
#endregion
}
static void Main(string[] args)
{
string strConn = "connection string to sql";
string direktorij = #"d:";
SqlConnection conn = new SqlConnection(strConn);
SqlCommand command = new SqlCommand("sp_ado_pos_data", conn);
command.CommandType = CommandType.StoredProcedure;
command.Parameters.Add('#skl_id', SqlDbType.Int).Value = 158;
SqlDataAdapter adapter = new SqlDataAdapter(command);
DataSet ds = new DataSet();
adapter.Fill(ds);
for (int i = 0; i < ds.Tables.Count; i++)
{
string datoteka = (string.Format(#"{0}tablea{1}.csv", direktorij, i));
DataTable tabela = ds.Tables[i];
CreateCSVFile(tabela,datoteka );
Console.WriteLine("GeneriĊĦem tabelu {0}", datoteka);
}
Console.ReadKey();
}
}
}
public void CreateCSVFile(DataTable dt, string strFilePath,string separator)
{
#region Export Grid to CSV
// Create the CSV file to which grid data will be exported.
StreamWriter sw = new StreamWriter(strFilePath, false);
int iColCount = dt.Columns.Count;
for (int i = 0; i < iColCount; i++)
{
sw.Write(dt.Columns[i]);
if (i < iColCount - 1)
{
sw.Write(separator);
}
}
sw.Write(sw.NewLine);
// Now write all the rows.
foreach (DataRow dr in dt.Rows)
{
for (int i = 0; i < iColCount; i++)
{
if (!Convert.IsDBNull(dr[i]))
{
sw.Write(dr[i].ToString());
}
if (i < iColCount - 1)
{
sw.Write(separator);
}
}
sw.Write(sw.NewLine);
}
sw.Close();
#endregion
}
BFree's answer worked for me. I needed to post the stream right to the browser. Which I'd imagine is a common alternative. I added the following to BFree's Main() code to do this:
//StreamReader reader = new StreamReader(stream);
//Console.WriteLine(reader.ReadToEnd());
string fileName = "fileName.csv";
HttpContext.Current.Response.ContentType = "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet";
HttpContext.Current.Response.AddHeader("content-disposition", string.Format("attachment;filename={0}", fileName));
stream.Position = 0;
stream.WriteTo(HttpContext.Current.Response.OutputStream);