The following code writes the data and is working fine, but I want to add more than one client (maybe 10) in the .csv file. How can I achieve this. Thanks in advance.
private void createFileButton_Click(object sender, EventArgs e)
{
string newFileName = "C:\\client_20100913.csv";
string clientDetails = clientNameTextBox.Text + "," + mIDTextBox.Text + "," + billToTextBox.Text;
//Header of the .csv File
string clientHeader = "Client Name(ie. Billto_desc)" + "," + "Mid_id,billing number(ie billto_id)" + "," + "business unit id" + Environment.NewLine;
File.WriteAllText(newFileName, clientHeader);
File.AppendAllText(newFileName, clientDetails);
MessageBox.Show("Client Added", "Added", MessageBoxButtons.OK);
}
If you want to append the client information to an existing file, how about:
string newFileName = "C:\\client_20100913.csv";
string clientDetails = clientNameTextBox.Text + "," + mIDTextBox.Text + "," + billToTextBox.Text;
if (!File.Exists(newFileName))
{
string clientHeader = "Client Name(ie. Billto_desc)" + "," + "Mid_id,billing number(ie billto_id)" + "," + "business unit id" + Environment.NewLine;
File.WriteAllText(newFileName, clientHeader);
}
File.AppendAllText(newFileName, clientDetails);
This way the header line is only written the first time, when the file is created.
Although it would probably be even nicer to provide a list-detail view that lets you view all clients, add and remove clients, select a client to edit details, and save the complete file with all clients.
It looks to me like you want a new client to be added every time you click the button.
If that's the case, the reason why it doesn't work currently is that the file is being cleared by the line
File.WriteAllText(newFileName, clientHeader);
The simplest change would be to check if the file exists before writing over it:
if (!File.Exists(newFileName))
{
//Header of the .csv File
string clientHeader = "Client Name(ie. Billto_desc)" + "," + "Mid_id,billing number(ie billto_id)" + "," + "business unit id" + Environment.NewLine;
File.WriteAllText(newFileName, clientHeader);
}
Although you could use other strategies, such as creating the file on startup of the application and keeping it open (using something like a StreamWriter). You would then close the writer when your application exited. This would pretty much guarantee that the file couldn't be messed with while your application is open.
You might want to do this because there is a race condition in that code - after you check the file exists, and before you write to the file, a user could delete it. Keeping the file open helps to avoid this, but you may or may not want to do it.
The underlying problem here seems to be where you're getting the data from to append to your CSV file. Your example code looks like it gets the various pieces of data from text boxes on the page, so if you want multiple clients, are they all going to have their data on the screen in text boxes? My instinct is probably not.
It sounds to me like you should be handling this client data using a class of some sort (perhaps persisted in a database) and then implement a method in the class called something like void AppendToCSV(string filename), which appends that client data to the CSV file. Then you can loop over your client objects, appending each one in turn.
How you produce/store your client objects, in relation to the text boxes you have on the screen, depends on what your app is trying to achieve.
I know this has been answered but there is what i did to create a "log" of subscribers. This uses reflection to get the properties and values of the object. Hope this helps someone in the future.
internal static bool UpdateSubscriberList(MailingListEmail subscriber)
{
PropertyInfo[] propertyinfo;
propertyinfo = typeof(MailingListEmail).GetProperties();
var values = string.Empty;
try
{
string fileName = #"C:\Development\test.csv";
if (!File.Exists(fileName))
{
var header = string.Empty;
foreach (var prop in propertyinfo)
{
if (string.IsNullOrEmpty(header))
header += prop.Name;
else
header = string.Format("{0},{1}", header, prop.Name);
}
header = string.Format("{0},{1}", header, "CreatedDate");
header += Environment.NewLine;
File.WriteAllText(fileName, header);
}
foreach (var prop in propertyinfo)
{
var value = prop.GetValue(subscriber, null);
if (string.IsNullOrEmpty(values))
values += value;
else
values = string.Format("{0},{1}", values, value);
}
values = string.Format("{0},{1}", values, DateTime.Now.ToShortDateString());
values += Environment.NewLine;
File.AppendAllText(fileName, values);
}
catch (Exception ex)
{
Elmah.ErrorSignal.FromCurrentContext().Raise(ex);
return false;
}
return true;
}
here is what i have done, and it works for me perfectly :
first you need to creat DataTable from your listview, or just put data from textboxes:
`public Boolean PreparCVS(string DateOne, string DataTwo)
{
try
{
// Create the `DataTable` structure according to your data source
DataTable table = new DataTable();
table.Columns.Add("HeaderOne", typeof(string));
table.Columns.Add("HeaderTwo", typeof(String));
// Iterate through data source object and fill the table
table.Rows.Add(HeaderOne, HeaderTwo);
//Creat CSV File
CreateCSVFile(table, sCsvFilePath);
return true;
}
catch (Exception ex)
{
throw new System.Exception(ex.Message);
}
}`
once dataTable is created you can generate CSV file by this method :
in the streamwriter constructor you must specify in the second parameter True, by this, you can append data to you existing .csv file :
public void CreateCSVFile(DataTable dt, string strFilePath)
{
StreamWriter sw = new StreamWriter(strFilePath, true);
int iColCount = dt.Columns.Count;
for (int i = 0; i < iColCount; i++)
{
sw.Write(dt.Columns[i]);
if (i < iColCount - 1)
{
sw.Write(",");
}
}
sw.Write(sw.NewLine);
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();
}
// At first read all the data from your first CSV
StreamReader oStreamReader = new StreamReader(#"d:\test\SourceFile.csv");
string recordsFromFirstFile = oStreamReader.ReadToEnd();
oStreamReader.Close();
// Now read the new records from your another csv file
oStreamReader = new StreamReader(#"d:\test\DestinationFile.csv");
string recordsFromSecondFile = oStreamReader.ReadToEnd();
oStreamReader.Close();
oStreamReader.Dispose();
// Here Records from second file will also contain column headers so we need to truncate them using Substring() method
recordsFromSecondFile = recordsFromSecondFile.Substring(recordsFromSecondFile.IndexOf('\n') + 1);
// Now merge the records either in SourceFile.csv or in Targetfile.csv or as per your required file
StreamWriter oStreamWriter= new StreamWriter(#"d:\testdata\TargetFile.csv");
oStreamWriter.Write(recordsFromFirstFile + recordsFromSecondFile);
oStreamWriter.Close();
oStreamWriter.Dispose();
Happy Coding.....
c#csv
using CsvHelper;
public void WriteDataToCsv(MsgEnvironmentData[] data, string csvPath)
{
if (!File.Exists(csvPath))
{
using (var writer = new StreamWriter(csvPath))
using (var csvWriter = new CsvWriter(writer,firstConfiguration))
{
csvWriter.WriteHeader<MsgEnvironmentData>();
csvWriter.NextRecord();
csvWriter.WriteRecords(data);
}
}
else
{
using (var stream = new FileStream(csvPath, FileMode.Append))
using (var writer = new StreamWriter(stream))
using (var csvWriter = new CsvWriter(writer, secondConfiguration))
{
csvWriter.WriteRecords(data);
}
}
}
Jeramy's answer writing the contents on last cell and from their horizontally in a row in csv file. I mixed and matched his solution with answer given here. I know this questions been asked long before but for the ones who doing research I'm posting the answer here.
string newFileName = #"C:\.NET\test.csv"; //filepath
var csv = new StringBuilder();
string clientDetails = "content1,content2,content3" + Environment.NewLine;
csv.Append(clientDetails);
File.AppendAllText(newFileName, csv.ToString());
I use this simple piece of code to append data to an existing CSV file:
string[] data = { "John", "Doe", "25" };
string csvFilePath = "example.csv";
// Open the file for writing
using (StreamWriter writer = File.AppendText(csvFilePath))
{
// Write the data row
writer.WriteLine(string.Join(",", data));
}
Related
I am trying to write into a csv file row by row using C# language. Here is my function
string first = reader[0].ToString();
string second=image.ToString();
string csv = string.Format("{0},{1}\n", first, second);
File.WriteAllText(filePath, csv);
The whole function runs inside a loop, and every row should be written to the csv file. In my case, next row overwrites the existing row and in the end, I am getting an only single record in the csv file which is the last one. How can I write all the rows in the csv file?
UPDATE
Back in my naïve days, I suggested doing this manually (it was a simple solution to a simple question), however due to this becoming more and more popular, I'd recommend using the library CsvHelper that does all the safety checks, etc.
CSV is way more complicated than what the question/answer suggests.
Original Answer
As you already have a loop, consider doing it like this:
//before your loop
var csv = new StringBuilder();
//in your loop
var first = reader[0].ToString();
var second = image.ToString();
//Suggestion made by KyleMit
var newLine = string.Format("{0},{1}", first, second);
csv.AppendLine(newLine);
//after your loop
File.WriteAllText(filePath, csv.ToString());
Or something to this effect.
My reasoning is: you won't be need to write to the file for every item, you will only be opening the stream once and then writing to it.
You can replace
File.WriteAllText(filePath, csv.ToString());
with
File.AppendAllText(filePath, csv.ToString());
if you want to keep previous versions of csv in the same file
C# 6
If you are using c# 6.0 then you can do the following
var newLine = $"{first},{second}"
EDIT
Here is a link to a question that explains what Environment.NewLine does.
I would highly recommend you to go the more tedious route. Especially if your file size is large.
using(var w = new StreamWriter(path))
{
for( /* your loop */)
{
var first = yourFnToGetFirst();
var second = yourFnToGetSecond();
var line = string.Format("{0},{1}", first, second);
w.WriteLine(line);
w.Flush();
}
}
File.AppendAllText() opens a new file, writes the content and then closes the file. Opening files is a much resource-heavy operation, than writing data into open stream. Opening\closing a file inside a loop will cause performance drop.
The approach suggested by Johan solves that problem by storing all the output in memory and then writing it once. However (in case of big files) you program will consume a large amount of RAM and even crash with OutOfMemoryException
Another advantage of my solution is that you can implement pausing\resuming by saving current position in input data.
upd. Placed using in the right place
Writing csv files by hand can be difficult because your data might contain commas and newlines. I suggest you use an existing library instead.
This question mentions a few options.
Are there any CSV readers/writer libraries in C#?
I use a two parse solution as it's very easy to maintain
// Prepare the values
var allLines = (from trade in proposedTrades
select new object[]
{
trade.TradeType.ToString(),
trade.AccountReference,
trade.SecurityCodeType.ToString(),
trade.SecurityCode,
trade.ClientReference,
trade.TradeCurrency,
trade.AmountDenomination.ToString(),
trade.Amount,
trade.Units,
trade.Percentage,
trade.SettlementCurrency,
trade.FOP,
trade.ClientSettlementAccount,
string.Format("\"{0}\"", trade.Notes),
}).ToList();
// Build the file content
var csv = new StringBuilder();
allLines.ForEach(line =>
{
csv.AppendLine(string.Join(",", line));
});
File.WriteAllText(filePath, csv.ToString());
Instead of calling every time AppendAllText() you could think about opening the file once and then write the whole content once:
var file = #"C:\myOutput.csv";
using (var stream = File.CreateText(file))
{
for (int i = 0; i < reader.Count(); i++)
{
string first = reader[i].ToString();
string second = image.ToString();
string csvRow = string.Format("{0},{1}", first, second);
stream.WriteLine(csvRow);
}
}
You can use AppendAllText instead:
File.AppendAllText(filePath, csv);
As the documentation of WriteAllText says:
If the target file already exists, it is overwritten
Also, note that your current code is not using proper new lines, for example in Notepad you'll see it all as one long line. Change the code to this to have proper new lines:
string csv = string.Format("{0},{1}{2}", first, image, Environment.NewLine);
Instead of reinventing the wheel a library could be used. CsvHelper is great for creating and reading csv files. It's read and write operations are stream based and therefore also support operations with a big amount of data.
You can write your csv like the following.
using(var textWriter = new StreamWriter(#"C:\mypath\myfile.csv"))
{
var writer = new CsvWriter(textWriter, CultureInfo.InvariantCulture);
writer.Configuration.Delimiter = ",";
foreach (var item in list)
{
writer.WriteField( "a" );
writer.WriteField( 2 );
writer.WriteField( true );
writer.NextRecord();
}
}
As the library is using reflection it will take any type and parse it directly.
public class CsvRow
{
public string Column1 { get; set; }
public bool Column2 { get; set; }
public CsvRow(string column1, bool column2)
{
Column1 = column1;
Column2 = column2;
}
}
IEnumerable<CsvRow> rows = new [] {
new CsvRow("value1", true),
new CsvRow("value2", false)
};
using(var textWriter = new StreamWriter(#"C:\mypath\myfile.csv")
{
var writer = new CsvWriter(textWriter, CultureInfo.InvariantCulture);
writer.Configuration.Delimiter = ",";
writer.WriteRecords(rows);
}
value1,true
value2,false
If you want to read more about the librarys configurations and possibilities you can do so here.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.UI;
using System.Web.UI.WebControls;
using System.Data;
using System.Configuration;
using System.Data.SqlClient;
public partial class CS : System.Web.UI.Page
{
protected void ExportCSV(object sender, EventArgs e)
{
string constr = ConfigurationManager.ConnectionStrings["constr"].ConnectionString;
using (SqlConnection con = new SqlConnection(constr))
{
using (SqlCommand cmd = new SqlCommand("SELECT * FROM Customers"))
{
using (SqlDataAdapter sda = new SqlDataAdapter())
{
cmd.Connection = con;
sda.SelectCommand = cmd;
using (DataTable dt = new DataTable())
{
sda.Fill(dt);
//Build the CSV file data as a Comma separated string.
string csv = string.Empty;
foreach (DataColumn column in dt.Columns)
{
//Add the Header row for CSV file.
csv += column.ColumnName + ',';
}
//Add new line.
csv += "\r\n";
foreach (DataRow row in dt.Rows)
{
foreach (DataColumn column in dt.Columns)
{
//Add the Data rows.
csv += row[column.ColumnName].ToString().Replace(",", ";") + ',';
}
//Add new line.
csv += "\r\n";
}
//Download the CSV file.
Response.Clear();
Response.Buffer = true;
Response.AddHeader("content-disposition", "attachment;filename=SqlExport.csv");
Response.Charset = "";
Response.ContentType = "application/text";
Response.Output.Write(csv);
Response.Flush();
Response.End();
}
}
}
}
}
}
Handling Commas
For handling commas inside of values when using string.Format(...), the following has worked for me:
var newLine = string.Format("\"{0}\",\"{1}\",\"{2}\"",
first,
second,
third
);
csv.AppendLine(newLine);
So to combine it with Johan's answer, it'd look like this:
//before your loop
var csv = new StringBuilder();
//in your loop
var first = reader[0].ToString();
var second = image.ToString();
//Suggestion made by KyleMit
var newLine = string.Format("\"{0}\",\"{1}\"", first, second);
csv.AppendLine(newLine);
//after your loop
File.WriteAllText(filePath, csv.ToString());
Returning CSV File
If you simply wanted to return the file instead of writing it to a location, this is an example of how I accomplished it:
From a Stored Procedure
public FileContentResults DownloadCSV()
{
// I have a stored procedure that queries the information I need
SqlConnection thisConnection = new SqlConnection("Data Source=sv12sql;User ID=UI_Readonly;Password=SuperSecure;Initial Catalog=DB_Name;Integrated Security=false");
SqlCommand queryCommand = new SqlCommand("spc_GetInfoINeed", thisConnection);
queryCommand.CommandType = CommandType.StoredProcedure;
StringBuilder sbRtn = new StringBuilder();
// If you want headers for your file
var header = string.Format("\"{0}\",\"{1}\",\"{2}\"",
"Name",
"Address",
"Phone Number"
);
sbRtn.AppendLine(header);
// Open Database Connection
thisConnection.Open();
using (SqlDataReader rdr = queryCommand.ExecuteReader())
{
while (rdr.Read())
{
// rdr["COLUMN NAME"].ToString();
var queryResults = string.Format("\"{0}\",\"{1}\",\"{2}\"",
rdr["Name"].ToString(),
rdr["Address"}.ToString(),
rdr["Phone Number"].ToString()
);
sbRtn.AppendLine(queryResults);
}
}
thisConnection.Close();
return File(new System.Text.UTF8Encoding().GetBytes(sbRtn.ToString()), "text/csv", "FileName.csv");
}
From a List
/* To help illustrate */
public static List<Person> list = new List<Person>();
/* To help illustrate */
public class Person
{
public string name;
public string address;
public string phoneNumber;
}
/* The important part */
public FileContentResults DownloadCSV()
{
StringBuilder sbRtn = new StringBuilder();
// If you want headers for your file
var header = string.Format("\"{0}\",\"{1}\",\"{2}\"",
"Name",
"Address",
"Phone Number"
);
sbRtn.AppendLine(header);
foreach (var item in list)
{
var listResults = string.Format("\"{0}\",\"{1}\",\"{2}\"",
item.name,
item.address,
item.phoneNumber
);
sbRtn.AppendLine(listResults);
}
}
return File(new System.Text.UTF8Encoding().GetBytes(sbRtn.ToString()), "text/csv", "FileName.csv");
}
Hopefully this is helpful.
This is a simple tutorial on creating csv files using C# that you will be able to edit and expand on to fit your own needs.
First you’ll need to create a new Visual Studio C# console application, there are steps to follow to do this.
The example code will create a csv file called MyTest.csv in the location you specify. The contents of the file should be 3 named columns with text in the first 3 rows.
https://tidbytez.com/2018/02/06/how-to-create-a-csv-file-with-c/
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.IO;
namespace CreateCsv
{
class Program
{
static void Main()
{
// Set the path and filename variable "path", filename being MyTest.csv in this example.
// Change SomeGuy for your username.
string path = #"C:\Users\SomeGuy\Desktop\MyTest.csv";
// Set the variable "delimiter" to ", ".
string delimiter = ", ";
// This text is added only once to the file.
if (!File.Exists(path))
{
// Create a file to write to.
string createText = "Column 1 Name" + delimiter + "Column 2 Name" + delimiter + "Column 3 Name" + delimiter + Environment.NewLine;
File.WriteAllText(path, createText);
}
// This text is always added, making the file longer over time
// if it is not deleted.
string appendText = "This is text for Column 1" + delimiter + "This is text for Column 2" + delimiter + "This is text for Column 3" + delimiter + Environment.NewLine;
File.AppendAllText(path, appendText);
// Open the file to read from.
string readText = File.ReadAllText(path);
Console.WriteLine(readText);
}
}
}
public static class Extensions
{
public static void WriteCSVLine(this StreamWriter writer, IEnumerable<string> fields)
{
const string q = #"""";
writer.WriteLine(string.Join(",",
fields.Select(
v => (v.Contains(',') || v.Contains('"') || v.Contains('\n') || v.Contains('\r')) ? $"{q}{v.Replace(q, q + q)}{q}" : v
)));
}
public static void WriteCSVLine(this StreamWriter writer, params string[] fields) => WriteCSVLine(writer, (IEnumerable<string>)fields);
}
This should allow you to write a csv file quite simply. Usage:
StreamWriter writer = new ("myfile.csv");
writer.WriteCSVLine("A", "B"); // A,B
Here is another open source library to create CSV file easily, Cinchoo ETL
List<dynamic> objs = new List<dynamic>();
dynamic rec1 = new ExpandoObject();
rec1.Id = 10;
rec1.Name = #"Mark";
rec1.JoinedDate = new DateTime(2001, 2, 2);
rec1.IsActive = true;
rec1.Salary = new ChoCurrency(100000);
objs.Add(rec1);
dynamic rec2 = new ExpandoObject();
rec2.Id = 200;
rec2.Name = "Tom";
rec2.JoinedDate = new DateTime(1990, 10, 23);
rec2.IsActive = false;
rec2.Salary = new ChoCurrency(150000);
objs.Add(rec2);
using (var parser = new ChoCSVWriter("emp.csv").WithFirstLineHeader())
{
parser.Write(objs);
}
For more information, please read the CodeProject article on usage.
One simple way to get rid of the overwriting issue is to use File.AppendText to append line at the end of the file as
void Main()
{
using (System.IO.StreamWriter sw = System.IO.File.AppendText("file.txt"))
{
string first = reader[0].ToString();
string second=image.ToString();
string csv = string.Format("{0},{1}\n", first, second);
sw.WriteLine(csv);
}
}
enter code here
string string_value= string.Empty;
for (int i = 0; i < ur_grid.Rows.Count; i++)
{
for (int j = 0; j < ur_grid.Rows[i].Cells.Count; j++)
{
if (!string.IsNullOrEmpty(ur_grid.Rows[i].Cells[j].Text.ToString()))
{
if (j > 0)
string_value= string_value+ "," + ur_grid.Rows[i].Cells[j].Text.ToString();
else
{
if (string.IsNullOrEmpty(string_value))
string_value= ur_grid.Rows[i].Cells[j].Text.ToString();
else
string_value= string_value+ Environment.NewLine + ur_grid.Rows[i].Cells[j].Text.ToString();
}
}
}
}
string where_to_save_file = #"d:\location\Files\sample.csv";
File.WriteAllText(where_to_save_file, string_value);
string server_path = "/site/Files/sample.csv";
Response.ContentType = ContentType;
Response.AppendHeader("Content-Disposition", "attachment; filename=" + Path.GetFileName(server_path));
Response.WriteFile(server_path);
Response.End();
You might just have to add a line feed "\n\r".
I have an application that runs with and without a GUI depending on the user. I need it to export the DataGrid as a CSV file, heres the code:
// Copy contents of datagrid to clipboard, including header.
mainDataGrid.SelectAllCells();
mainDataGrid.ClipboardCopyMode = DataGridClipboardCopyMode.IncludeHeader;
ApplicationCommands.Copy.Execute(null, mainDataGrid);
string result = (string)Clipboard.GetData(DataFormats.CommaSeparatedValue);
This works fine with the GUI. The problem happens when I don't have a GUI, I think this is because the clipboard can't copy something that isn't there. Is there a different way to export it or is there a way to set the clipboards data instead of executing a copy command?
In order to "copy something that isn't there", you'll need to use the underlying data object that is your DataGrid's ItemsSource.
If your DataGrid's ItemsSource is a DataTable, this method, analogous to MosesTheHoly's, will return a CSV string, but in a simpler manner.
public string GetCSVFromDataTabe(DataTable datatable)
{
StringBuilder stringBuilder = new StringBuilder();
stringBuilder.Append(string.Join(",", dt.Columns.ToList<DataColumn>().Select(column => column.ColumnName).ToList()) + "\n");
dataTable.Rows.ToList<DataRow>().ForEach(row => sb.Append(string.Join(",", row.ItemArray) + "\n"));
return stringBuilder.ToString();
}
That should do it. Fairly concise, in my opinion. Now, just write that string to whatever file you need.
Alright, the code could probably be shortened, but this is the way I did it. I created a function that takes in a datatable and returns a CSV string.
private void SaveToCSV() {
DataTable dt = new DataTable();
dt = ((DataView)mainDataGrid.ItemsSource).ToTable();
string result = WriteDataTable(dt);
// The File.Create().Close() is so it closes the filestream after it creates it.
if (!File.Exists(CSVFilePath)) {
File.Create(CSVFilePath).Close();
}
File.AppendAllText(CSVFilePath, result, UnicodeEncoding.UTF8);
}
private string WriteDataTable(DataTable dataTable) {
string output = "";
// Need to get the last column so I know when to add a new line instead of comma.
string lastColumnName = dataTable.Columns[dataTable.Columns.Count - 1].ColumnName;
// Get the headers from the datatable.
foreach (DataColumn column in dataTable.Columns) {
if (lastColumnName != column.ColumnName) {
output += (column.ColumnName.ToString() + ",");
}
else {
output += (column.ColumnName.ToString() + "\n");
}
}
// Get the actual data from the datatable.
foreach (DataRow row in dataTable.Rows) {
foreach (DataColumn column in dataTable.Columns) {
if (lastColumnName != column.ColumnName) {
output += (row[column].ToString() + ",");
}
else {
output += (row[column].ToString() + "\n");
}
}
}
return output;
}
I am trying to implement a script in my application that will dump the entire contents (for now, but I am trying to write the code so that I can easily customize it to only grab certain columns) of a sql db (running ms sql server express 2014) to a .csv file.
Here is the code I have written currently:
public void doCsvWrite(string timeStamp){
try {
//specify file name of log file (csv).
string newFileName = "C:/TestDirectory/DataExport-" + timeStamp + ".csv";
//check to see if file exists, if not create an empty file with the specified file name.
if (!File.Exists(newFileName)) {
FileStream fs = new FileStream(newFileName, FileMode.CreateNew);
fs.Close();
//define header of new file, and write header to file.
string csvHeader = "ITEM1,ITEM2,ITEM3,ITEM4,ITEM5";
using (FileStream fsWHT = new FileStream(newFileName, FileMode.Append, FileAccess.Write))
using(StreamWriter swT = new StreamWriter(fsWHT))
{
swT.WriteLine(csvHeader.ToString());
}
}
//set up connection to database.
SqlConnection myDEConnection;
String cDEString = "Data Source=localhost\\NAMEDPIPE;Initial Catalog=db;User Id=user;Password=pwd";
String strDEStatement = "SELECT * FROM table";
try
{
myDEConnection = new SqlConnection(cDEString);
}
catch (Exception ex)
{
//error handling here.
return;
}
try
{
myDEConnection.Open();
}
catch (Exception ex)
{
//error handling here.
return;
}
SqlDataReader reader = null;
SqlCommand myDECommand = new SqlCommand(strDEStatement, myDEConnection);
try
{
reader = myDECommand.ExecuteReader();
while (reader.Read())
{
for (int i = 0; i < reader.FieldCount; i++)
{
if(reader["Column1"].ToString() == "") {
//does nothing if the current line is "bugged" (containing no values at all, typically happens after reboot of 3rd party equipment).
}
else {
//grab relevant tag data and set the csv line for the current row.
string csvDetails = reader["Column1"] + "," + reader["Column2"] + "," + String.Format("{0:0.0}", reader["Column3"]) + "," + String.Format("{0:0.000}", reader["Column4"]) + "," + reader["Column5"];
using (FileStream fsWDT = new FileStream(newFileName, FileMode.Append, FileAccess.Write))
using(StreamWriter swDT = new StreamWriter(fsWDT))
{
//write csv line to file.
swDT.WriteLine(csvDetails.ToString());
}
}
}
}
}
catch (Exception ex)
{
//error handling here.
myDEConnection.Close();
return;
}
myDEConnection.Close();
}
catch (Exception ex)
{
//error handling here.
MessageBox.Show(ex.Message);
}
}
Now, this was working fine when I was using it with a 3rd party SQLite-based database, but the output I'm getting after modifing this to my MSSQL db looks something like this (ITEM1 is the primary key, a standard auto-incrementing ID-field):
ITEM1,ITEM2,ITEM3,ITEM4,ITEM5
1,row1_item2,row1_item3,row1_item4,row1_item5
1,row1_item2,row1_item3,row1_item4,row1_item5
1,row1_item2,row1_item3,row1_item4,row1_item5
1,row1_item2,row1_item3,row1_item4,row1_item5
1,row1_item2,row1_item3,row1_item4,row1_item5
1,row1_item2,row1_item3,row1_item4,row1_item5
2,row2_item2,row2_item3,row2_item4,row2_item5
2,row2_item2,row2_item3,row2_item4,row2_item5
2,row2_item2,row2_item3,row2_item4,row2_item5
2,row2_item2,row2_item3,row2_item4,row2_item5
2,row2_item2,row2_item3,row2_item4,row2_item5
3,row3_item2,row3_item3,row3_item4,row3_item5
3,row3_item2,row3_item3,row3_item4,row3_item5
3,row3_item2,row3_item3,row3_item4,row3_item5
3,row3_item2,row3_item3,row3_item4,row3_item5
....
So it seems that it writes several entries of the same row, where I would just like one single line each row. Any suggestions?
Thanks in advance.
edit: Thanks everyone for your answers!
The for loop isn't needed in the section below. Because it loops from 0 to FieldCount I assume the loop was originally meant to append the text from each column together but inside the loop there's a single line that concatenates the text and assigns it to csvDetails.
try
{
reader = myDECommand.ExecuteReader();
while (reader.Read())
{
for (int i = 0; i < reader.FieldCount; i++)
{
if(reader["Column1"].ToString() == "") {
//does nothing if the current line is "bugged" (containing no values at all, typically happens after reboot of 3rd party equipment).
}
else {
//grab relevant tag data and set the csv line for the current row.
string csvDetails = reader["Column1"] + "," + reader["Column2"] + "," + String.Format("{0:0.0}", reader["Column3"]) + "," + String.Format("{0:0.000}", reader["Column4"]) + "," + reader["Column5"];
using (FileStream fsWDT = new FileStream(newFileName, FileMode.Append, FileAccess.Write))
using(StreamWriter swDT = new StreamWriter(fsWDT))
{
//write csv line to file.
swDT.WriteLine(csvDetails.ToString());
}
}
}
}
}
Usually, we use specialy designed export/import utilites for dumping data.
However, if you have to implement you own routine I suggest decomposing.
private static IEnumerable<IDataRecord> SourceData(String sql) {
using (SqlConnection con = new SqlConnection(ConnectionStringHere)) {
con.Open();
using (SqlCommand q = new SqlCommand(sql, con)) {
using (var reader = q.ExecuteReader()) {
while (reader.Read()) {
//TODO: you may want to add additional conditions here
yield return reader;
}
}
}
}
}
private static IEnumerable<String> ToCsv(IEnumerable<IDataRecord> data) {
foreach (IDataRecord record in data) {
StringBuilder sb = new StringBuilder();
for (int i = 0; i < record .FieldCount; ++i) {
String chunk = Convert.ToString(record .GetValue(0));
if (i > 0)
sb.Append(',');
if (chunk.Contains(',') || chunk.Contains(';'))
chunk = "\"" + chunk.Replace("\"", "\"\"") + "\"";
sb.Append(chunk);
}
yield return sb.ToString();
}
}
Having SourceData and ToCsv you can easily implement
private static void WriteMyCsv(String fileName) {
var source = SourceData("SELECT * FROM table");
File.WriteAllLines(fileName, ToCsv(source));
}
You have a for loop which is looping over the fieldcount.
for (int i = 0; i < reader.FieldCount; i++)
I think it will work if you remove the loop as you don't need to iterate through the columns.
it happens because output placed inside for-loop
for (int i = 0; i < reader.FieldCount; i++)
and every record repeats FieldCount-times
Complete example. Verified working .NET 4.8, May 22. Code simplified for demo.
Why the DataTable ? Under circumstances it is useful. If you converting hundreds of files at once and multi threading - it works as large buffer + you can do pretty complex data mangling at the same time - should you need it.
UNFORTUNATELY - Microsoft trying to detect the column types and if your data not comply with the mechanism it ends with hard to correct errors. In that case use the second solution.
// Get the data from SQLite
SqliteConnection SQLiDataCon = new SqliteConnection(#"Data Source=c:\sqlite.db3");
SQLiDataCon.Open();
SqliteDataReader SQLiDtaReader = new SqliteCommand(#"SELECT * FROM stats;", SQLiDataCon).ExecuteReader();
// Load data to DataTable
DataTable csvTable = new DataTable();
csvTable.Load(SQLiDtaReader);
// Get "one" string with column names
string csvFields = #"""" + String.Join(#""",""",csvTable.Columns.Cast<DataColumn>().Select(dc => dc.ColumnName).ToArray()) + #"""";
// Prep "in memory the entire content of the CSV"
StringBuilder csvString = new StringBuilder();
// Write the header in
csvString.AppendLine(csvFields);
// Write the rows in
foreach (DataRow dr in csvTable.Rows)
{
csvString.AppendLine(#"""" + String.Join(#""",""", dr.ItemArray) + #"""");
}
// Save to file
StreamWriter csvFile = new StreamWriter(#"c:\stats.csv");
csvFile.Write(csvString);
Without DataTable.
// SQLITE
SqliteConnection SQLiDataCon = new SqliteConnection(#"Data Source=c:\sqlite.db3");
SQLiDataCon.Open();
StringBuilder csvString = new StringBuilder();
StreamWriter csvFile;
Object[] csvRow;
SqliteDataReader SQLiDtaReader = new SqliteCommand(#"SELECT * FROM sometable;", SQLiDataCon).ExecuteReader();
// CSV HEADER
csvString.AppendLine(#"""" + String.Join(#""",""", SQLiDtaReader.GetSchemaTable().AsEnumerable().Select(dr => dr.Field<string>("ColumnName")).ToArray<string>()) + #"""");
// CSV BODY
while (SQLiDtaReader.Read())
{
SQLiDtaReader.GetValues(csvRow = new Object[SQLiDtaReader.FieldCount]);
csvString.AppendLine(#"""" + String.Join(#""",""",csvRow ) + #"""");
}
// WRITE IT
csvFile = new StreamWriter(#"C:\somecsvfile.csv");
csvFile.Write(csvString);
I'm working on a application which will export my DataGridView called scannerDataGridView to a csv file.
Found some example code to do this, but can't get it working. Btw my datagrid isn't databound to a source.
When i try to use the Streamwriter to only write the column headers everything goes well, but when i try to export the whole datagrid including data i get an exeption trhown.
System.NullReferenceException: Object reference not set to an instance
of an object. at Scanmonitor.Form1.button1_Click(Object sender,
EventArgs e)
Here is my Code, error is given on the following line:
dataFromGrid = dataFromGrid + ',' + dataRowObject.Cells[i].Value.ToString();
//csvFileWriter = StreamWriter
//scannerDataGridView = DataGridView
private void button1_Click(object sender, EventArgs e)
{
string CsvFpath = #"C:\scanner\CSV-EXPORT.csv";
try
{
System.IO.StreamWriter csvFileWriter = new StreamWriter(CsvFpath, false);
string columnHeaderText = "";
int countColumn = scannerDataGridView.ColumnCount - 1;
if (countColumn >= 0)
{
columnHeaderText = scannerDataGridView.Columns[0].HeaderText;
}
for (int i = 1; i <= countColumn; i++)
{
columnHeaderText = columnHeaderText + ',' + scannerDataGridView.Columns[i].HeaderText;
}
csvFileWriter.WriteLine(columnHeaderText);
foreach (DataGridViewRow dataRowObject in scannerDataGridView.Rows)
{
if (!dataRowObject.IsNewRow)
{
string dataFromGrid = "";
dataFromGrid = dataRowObject.Cells[0].Value.ToString();
for (int i = 1; i <= countColumn; i++)
{
dataFromGrid = dataFromGrid + ',' + dataRowObject.Cells[i].Value.ToString();
csvFileWriter.WriteLine(dataFromGrid);
}
}
}
csvFileWriter.Flush();
csvFileWriter.Close();
}
catch (Exception exceptionObject)
{
MessageBox.Show(exceptionObject.ToString());
}
LINQ FTW!
var sb = new StringBuilder();
var headers = dataGridView1.Columns.Cast<DataGridViewColumn>();
sb.AppendLine(string.Join(",", headers.Select(column => "\"" + column.HeaderText + "\"").ToArray()));
foreach (DataGridViewRow row in dataGridView1.Rows)
{
var cells = row.Cells.Cast<DataGridViewCell>();
sb.AppendLine(string.Join(",", cells.Select(cell => "\"" + cell.Value + "\"").ToArray()));
}
And indeed, c.Value.ToString() will throw on null value, while c.Value will correctly convert to an empty string.
A little known feature of the DataGridView is the ability to programmatically select some or all of the DataGridCells, and send them to a DataObject using the method DataGridView.GetClipboardContent(). Whats the advantage of this then?
A DataObject doesn't just store an object, but rather the representation of that object in various different formats. This is how the Clipboard is able to work its magic; it has various formats stored and different controls/classes can specify which format they wish to accept. In this case, the DataGridView will store the selected cells in the DataObject as a tab-delimited text format, a CSV format, or as HTML (*).
The contents of the DataObject can be retrieved by calling the DataObject.GetData() or DataObject.GetText() methods and specifying a predefined data format enum. In this case, we want the format to be TextDataFormat.CommaSeparatedValue for CSV, then we can just write that result to a file using System.IO.File class.
(*) Actually, what it returns is not, strictly speaking, HTML. This format will also contain a data header that you were not expecting. While the header does contain the starting position of the HTML, I just discard anything above the HTML tag like myString.Substring(IndexOf("<HTML>"));.
Observe the following code:
void SaveDataGridViewToCSV(string filename)
{
// Choose whether to write header. Use EnableWithoutHeaderText instead to omit header.
dataGridView1.ClipboardCopyMode = DataGridViewClipboardCopyMode.EnableAlwaysIncludeHeaderText;
// Select all the cells
dataGridView1.SelectAll();
// Copy selected cells to DataObject
DataObject dataObject = dataGridView1.GetClipboardContent();
// Get the text of the DataObject, and serialize it to a file
File.WriteAllText(filename, dataObject.GetText(TextDataFormat.CommaSeparatedValue));
}
Now, isn't that better? Why re-invent the wheel?
Hope this helps...
Please check this code.its working fine
try
{
//Build the CSV file data as a Comma separated string.
string csv = string.Empty;
//Add the Header row for CSV file.
foreach (DataGridViewColumn column in dataGridView1.Columns)
{
csv += column.HeaderText + ',';
}
//Add new line.
csv += "\r\n";
//Adding the Rows
foreach (DataGridViewRow row in dataGridView1.Rows)
{
foreach (DataGridViewCell cell in row.Cells)
{
if (cell.Value != null)
{
//Add the Data rows.
csv += cell.Value.ToString().TrimEnd(',').Replace(",", ";") + ',';
}
// break;
}
//Add new line.
csv += "\r\n";
}
//Exporting to CSV.
string folderPath = "C:\\CSV\\";
if (!Directory.Exists(folderPath))
{
Directory.CreateDirectory(folderPath);
}
File.WriteAllText(folderPath + "Invoice.csv", csv);
MessageBox.Show("");
}
catch
{
MessageBox.Show("");
}
Found the problem, the coding was fine but i had an empty cell that gave the problem.
Your code was almost there... But I made the following corrections and it works great. Thanks for the post.
Error:
string[] output = new string[dgvLista_Apl_Geral.RowCount + 1];
Correction:
string[] output = new string[DGV.RowCount + 1];
Error:
System.IO.File.WriteAllLines(filename, output, System.Text.Encoding.UTF8);
Correction:
System.IO.File.WriteAllLines(sfd.FileName, output, System.Text.Encoding.UTF8);
The line "csvFileWriter.WriteLine(dataFromGrid);" should be moved down one line below the closing bracket, else you'll get a lot of repeating results:
for (int i = 1; i <= countColumn; i++)
{
dataFromGrid = dataFromGrid + ',' + dataRowObject.Cells[i].Value.ToString();
}
csvFileWriter.WriteLine(dataFromGrid);
I think this is the correct for your SaveToCSV function : ( otherwise Null ...)
for (int i = 0; i < columnCount; i++)
Not :
for (int i = 1; (i - 1) < DGV.RowCount; i++)
This is what I been using in my projects:
void export_csv(string file, DataGridView grid)
{
using (StreamWriter csv = new StreamWriter(file, false))
{
int totalcolms = grid.ColumnCount;
foreach (DataGridViewColumn colm in grid.Columns) csv.Write(colm.HeaderText + ',');
csv.Write('\n');
string data = "";
foreach (DataGridViewRow row in grid.Rows)
{
if (row.IsNewRow) continue;
data = "";
for (int i = 0; i < totalcolms; i++)
{
data += (row.Cells[i].Value ?? "").ToString() + ',';
}
if (data != string.Empty) csv.WriteLine(data);
}
}
}
Struggling with a C# Component. What I am trying to do is take a column that is ntext in my input source which is delimited with pipes, and then write the array to a text file. When I run my component my output looks like this:
DealerID,StockNumber,Option
161552,P1427,Microsoft.SqlServer.Dts.Pipeline.BlobColumn
Ive been working with the GetBlobData method and im struggling with it. Any help with be greatly appreciated! Here is the full script:
public override void Input0_ProcessInputRow(Input0Buffer Row)
{
string vehicleoptionsdelimited = Row.Options.ToString();
//string OptionBlob = Row.Options.GetBlobData(int ;
//string vehicleoptionsdelimited = System.Text.Encoding.GetEncoding(Row.Options.ColumnInfo.CodePage).GetChars(OptionBlob);
string[] option = vehicleoptionsdelimited.Split('|');
string path = #"C:\Users\User\Desktop\Local_DS_CSVs\";
string[] headerline =
{
"DealerID" + "," + "StockNumber" + "," + "Option"
};
System.IO.File.WriteAllLines(path + "OptionInput.txt", headerline);
using (System.IO.StreamWriter file = new System.IO.StreamWriter(path + "OptionInput.txt", true))
{
foreach (string s in option)
{
file.WriteLine(Row.DealerID.ToString() + "," + Row.StockNumber.ToString() + "," + s);
}
}
Try using
BlobToString(Row.Options)
using this function:
private string BlobToString(BlobColumn blob)
{
string result = "";
try
{
if (blob != null)
{
result = System.Text.Encoding.Unicode.GetString(blob.GetBlobData(0, Convert.ToInt32(blob.Length)));
}
}
catch (Exception ex)
{
result = ex.Message;
}
return result;
}
Adapted from:
http://mscrmtech.com/201001257/converting-microsoftsqlserverdtspipelineblobcolumn-to-string-in-ssis-using-c
Another very easy solution to this problem, because it is a total PITA, is to route the error output to a derived column component and cast your blob data to a to a STR or WSTR as a new column.
Route the output of that to your script component and the data will come in as an additional column on the pipeline ready for you to parse.
This will probably only work if your data is less than 8000 characters long.