I am new to C# and I have taken a small task on. The StackOverflow entry for reading a text file and saving to a list is a great start for me. I need to read a text file and send the data to an SQL database.
How to Read This Text File and store in a list using C#
The Data in List<Data> list = new List<Data>(); just keeps staying in red.
How can I stop this please?
I am a PLC engineer and I'm trying to collate data that cannot be handled by PLC. I am just trying to read the file so that I can then show the data in a Grid, with a view to populating the SQL database later.
The text file is held on a remote Linux machine. The collator is a WIndows 10 panel. The SQL can reside on teh Windows panel or remotely.
static void Main(string[] args)
{
List<Data> list = new List<Data>();
var dd = File.ReadAllLines(#"C:\Users\XXXX\Desktop\test.txt")
.Skip(1)
.Where(s => s.Length > 1).ToList();
foreach (var item in dd)
{
var columns = item.Split('\t').Where(c => c.Trim() != string.Empty).ToList();
if (columns != null && columns.Count > 0)
{
int id;
if (int.TryParse(columns[0], out id))
{
list.Add(new Data()
{
id = Convert.ToInt32(columns[0]),
Name = columns[1],
Description = columns[2],
Quantity = Convert.ToInt32(columns[3]),
Rate = Convert.ToDouble(columns[4]),
Discount = Convert.ToInt32(columns[5]),
Amount = int.Parse(columns[6])
});
}
else
{
list.Last().Description += columns[0];
}
}
}
Console.ReadLine();
}
I just keep receiving red squiggly lines on <Data. within Visual Studio
I got the code to work and I read the DAT/text file straight into a DatagridView. I am now writing the Grid to SQL.
Many thanks, sorry for latency as I've been away on-site.
String sLine = "";
try
{
//Pass the file you selected with the OpenFileDialog control to
//the StreamReader Constructor.
System.IO.StreamReader FileStream = new System.IO.StreamReader(openFileDialog1.FileName);
//You must set the value to false when you are programatically adding rows to
//a DataGridView. If you need to allow the user to add rows, you
//can set the value back to true after you have populated the DataGridView
dataGridView1.AllowUserToAddRows = false;
// Clear the DataGridView prior to reading a new text file
dataGridView1.Rows.Clear();
dataGridView1.Columns.Clear();
//Read the first line of the text file
sLine = FileStream.ReadLine();
//The Split Command splits a string into an array, based on the delimiter you pass.
//I chose to use a semi-colon for the text delimiter.
//Any character can be used as a delimeter in the split command.
//string[] s = sLine.Split(';');
string[] s = sLine.Split('\t');
//In this example, I placed the field names in the first row.
//The for loop below is used to create the columns and use the text values in
//the first row for the column headings.
for (int i = 0; i <= s.Count() - 1; i++)
{
DataGridViewColumn colHold = new DataGridViewTextBoxColumn();
colHold.Name = "col" + System.Convert.ToString(i);
colHold.HeaderText = s[i].ToString();
dataGridView1.Columns.Add(colHold);
}
//Read the next line in the text file in order to pass it to the
//while loop below
sLine = FileStream.ReadLine();
//The while loop reads each line of text.
while (sLine != null)
{
//Adds a new row to the DataGridView for each line of text.
dataGridView1.Rows.Add();
//This for loop loops through the array in order to retrieve each
//line of text.
for (int i = 0; i <= s.Count() - 1; i++)
{
//Splits each line in the text file into a string array
//s = sLine.Split(';');
s = sLine.Split('\t');
//Sets the value of the cell to the value of the text retreived from the text file.
dataGridView1.Rows[dataGridView1.Rows.Count - 1].Cells[i].Value = s[i].ToString();
}
sLine = FileStream.ReadLine();
}
//Close the selected text file.
FileStream.Close();
}
catch (Exception err)
{
//Display any errors in a Message Box.
System.Windows.Forms.MessageBox.Show("Error "+ err.Message, "Program Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
}
Related
enter image description here After import csv file to datagrid i need to add or insert new records to datagridview then export lastest data to csv file
after adding new data to datagrid imported data is not showing in datagrid only new data is showing
this is my code: I don't where I am going wrong.
-- here I need to add new data; and click on add; with new row.
namespace Bind_DataGridView_Using_DataTable
{
public partial class Bind_DataGridView_Using_DataTable : Form
{
public Bind_DataGridView_Using_DataTable()
{
InitializeComponent();
}
DataTable table = new DataTable();
int selectedRow;
private void Bind_DataGridView_Using_DataTable_Load(object sender, EventArgs e)
{
//Create Headers for the dataTable
table.Columns.Add("Id", typeof(int));
table.Columns.Add("FirstName", typeof(string));
table.Columns.Add("LastName", typeof(string));
table.Columns.Add("Profession", typeof(string));
dataGridView1.DataSource = table;
}
//Add to DataGridView
bool found = false;
private void BtnAdd_Click(object sender, EventArgs e)
{
if (!string.IsNullOrEmpty(IDTxt.Text))
{
if(dataGridView1.Rows.Count > 0)
{
foreach(DataGridViewRow row in dataGridView1.Rows)
{
if(Convert.ToString(row.Cells[0].Value) == IDTxt.Text)
{
found = true;
MessageBox.Show("Person Id already Exist");
}
}
if(!found)
{
table.Rows.Add(IDTxt.Text, fisrtTxt.Text, SurNameTxt.Text, ProfesTxt.Text);
dataGridView1.DataSource = table;
cleatTxts();
}
}
}
else if(string.IsNullOrEmpty(IDTxt.Text))
{
label1.Text = "Person Id should not be empty";
}
}
private void ImportBtn_Click(object sender, EventArgs e)
{
try
{
OpenFileDialog dlg = new OpenFileDialog();
if (DialogResult.OK == dlg.ShowDialog())
{
string path = dlg.FileName;
BindData(path);
MessageBox.Show("Import Action Compelted");
}
}
catch(Exception ex)
{
Console.WriteLine("The path does not exist", ex);
}
}
private void BindData(string filePath)
{
DataTable table = new DataTable();
string[] lines = System.IO.File.ReadAllLines(filePath);
if (lines.Length > 0)
{
//first line to create header
string firstLine = lines[0];
string[] headerLabels = firstLine.Split(',');
foreach (string headerWord in headerLabels)
{
table.Columns.Add(new DataColumn(headerWord));
}
//For Data
for (int i = 1; i < lines.Length; i++)
{
string[] dataWords = lines[i].Split(',');
DataRow dr = table.NewRow();
int columnIndex = 0;
foreach (string headerWord in headerLabels)
{
dr[headerWord] = dataWords[columnIndex++];
}
table.Rows.Add(dr);
}
dataGridView1.DataSource = table;
}
}
Atfter add new data imported data went i need want old and new data in datagrid
This i need to do but after adding only new data is coming output attcahed in other picture
As I commented, when the form loads, the code creates the “global” DataTable variable table. Inside the load event, the code creates the four columns and then sets the grids DataSource to this table. The form is displayed and we can see a grid with one “empty” new row displayed.
Then you click the ImportBtn button, it fires it click event, where the user picks a file, then this file path is passed to the method, BindData. Inside BindData …
The code “creates” a new LOCAL DataTable variable named table. Note, this is a different local table and NOT the table defined globally in the load event. The code creates the columns, add the rows, then sets this local table as a DataSource to the grid. When execution leaves the BindData method, the local variable table will go out of scope.
Therefore, after the imported data is displayed, we add some legal values to the text boxes and click the ADD button. In the BtnAdd_Click event, it uses the “global” variable table to add the row to. However, this table is empty because in the import code, it used a different local table. So when you add the row and set the grids DataSource to table… it will only have the one newly added row.
I will assume you do NOT want to create a new table variable in the BindData method. Instead we want to use the “global” DataTable variable table to add the imported rows to. This change is seen below. Here the changes are that instead of referencing the new row cells using the name, I used indexes. Also, a check is made to catch if the read row read from the csv file has fewer values than the data row has columns and vice versa. The code simply used the smaller of the two lengths to stay inbounds. totCols = Math.Min(dataRowCount, dataWords.Length);
private void BindData(string filePath) {
string[] lines = System.IO.File.ReadAllLines(filePath);
if (lines.Length > 0) {
int totCols;
int dataRowCount = table.Columns.Count;
string[] dataWords;
DataRow dr;
for (int i = 1; i < lines.Length; i++) {
dataWords = lines[i].Split(',');
dr = table.NewRow();
totCols = Math.Min(dataRowCount, dataWords.Length);
for (int colIndex = 0; colIndex < totCols; colIndex++) {
dr[colIndex] = dataWords[colIndex];
}
table.Rows.Add(dr);
}
}
}
Next in the ADD button click event, since the grid is already bound to the variable table, after we add the row, we simply call the tables AcceptChanges method and this should update the grid.
if (!found) {
table.Rows.Add(IDTxt.Text, fisrtTxt.Text, SurNameTxt.Text, ProfesTxt.Text);
table.AcceptChanges();
}
I hope this makes sense.
Edit update as per comment…
#Caius Jard … Thanks for the info. I switched the ReadAllLines to ReadLines and switched the type of lines to IEnumerable<string>. …
Before this change, I changed the code as you suggested with row.ItemArray = line.Split(','); and this failed. The error was that it did not like the string number when adding this to the first column Id which is an int column.
I copied the same code the OP used so the Id column in the table is an int type. Initially I though I must be using a string instead of an int, however this is not the case… So, this seems odd to me since the first code I posted in my answer assigns the value in column zero (0) as…
dr[colIndex] = dataWords[colIndex];
Since dataWords is a string[] array, I was wondering why I did not get the same error. From a couple of tests, it appears there is a conversion going on behind the scenes when the data is loaded from the CSV file and also when the row is added to the table in the ADD event. In the add event, the code is simply adding the row from the text boxes…
table.Rows.Add(IDTxt.Text, fisrtTxt.Text, SurNameTxt.Text, ProfesTxt.Text);
Here IDTxt.Text is obviously a string, however, if the string is a valid int, then this will get converted to an int when added. I would think this would throw the same error even if the text was a valid int.
This had me scratching my head as in my past experience, this would throw the grids DataError. At least I though it would. It appears it is ok and will do the behind-the-scenes conversion as long as the string is a valid int. I tested this in the BindData method, such that the Id was something like “a5.” When the table was filled, I got the format exception complaining about the bad int value.
So, in either case, checking for a valid int value would appear necessary to avoid getting these exceptions. In this case, when reading the file, the code below simply ignores rows with invalid Id values.
Also, if a line in the CSV has fewer values than the data row has columns, then that CSV row is ignored. If the line in the CSV has more values than the number of columns in the data row, then only the first n values are used. Where n is the number of columns in the data row.
In addition, when reading the CSV file, a check is made to see if there are “duplicate” Ids in the CSV file. If a duplicate Id is found in the CSV then, that row is ignored.
The int.TryParse method is used when reading the CSV file for converting the Id number from a string to an int. It is also used when adding a new row from the text boxes. If the Id text is not a valid int number, the row is not added.
The updated BindData method is below and renamed to AddCSVDataToGrid.
private void AddCSVDataToGrid(string filePath) {
IEnumerable<string> lines = File.ReadLines(filePath);
int tableColumnCount = table.Columns.Count;
string[] dataValues;
DataRow dr;
foreach (string line in lines.Skip(1)) { // <- Skip the header row
dataValues = line.Split(',');
if (dataValues.Length >= tableColumnCount) {
if (int.TryParse(dataValues[0], out int idValue)) { // <- check if Id is a valid int
if ((table.Select("Id = " + idValue)).Length == 0) { // <- check if Id is duplicate
dr = table.NewRow();
dr[0] = idValue;
for (int colIndex = 1; colIndex < tableColumnCount; colIndex++) {
dr[colIndex] = dataValues[colIndex];
}
table.Rows.Add(dr);
}
else {
// MessageBox.Show("Duplicate Id value in CSV file ..." + dataValues[0] + " - Skipping duplicate Id row");
}
}
else {
// MessageBox.Show("Person Id in CSV is not an integer..." + dataValues[0] + " Skiping invalid Id row");
}
}
else {
// MessageBox.Show("Missing data in CSV ..." + dataValues[0] + " - Skipping CSV row");
}
}
}
Next is the updated ADD button click event.
private void btnAdd_Click(object sender, EventArgs e) {
string idText = IDTxt.Text.Trim();
if (!string.IsNullOrEmpty(idText) && int.TryParse(idText, out int idValue)) {
if ((table.Select("Id = " + idValue)).Length == 0) {
table.Rows.Add(idValue, fisrtTxt.Text.Trim(), SurNameTxt.Text.Trim(), ProfesTxt.Text.Trim());
}
else {
MessageBox.Show("Person Id already Exist");
}
}
else {
label1.Text = "Person Id should not be empty && must be a valid int value";
}
}
I'm trying to get a csv file into a data table but there are some things in the csv file that I am trying to omit from being entered into the data table, so I wrote it to a list first.
The csv files that i will using the software for have different sections in it for which I then split the whole list into the separate lists for those sections.
After all that was achieved, i needed to skip some lines in each list and wrote the final form i was happy with to lists respective to previous set of lists.
Now I hit a wall, I need to write each of the lists to a respective data grid.
public partial class Form1 : Form
{
String filePath = "";
//list set 1 = list box
List<String> lines = new List<String>();
List<String> accountList = new List<String>();
List<String> statementList = new List<String>();
List<String> summaryList = new List<String>();
List<String> transactionList = new List<String>();
//list set 2 = dgv
List<String> accountList2 = new List<String>();
List<String> statementList2 = new List<String>();
List<String> summaryList2 = new List<String>();
List<String> transactionList2 = new List<String>();
public Form1()
{
InitializeComponent();
}
private void btn_find_Click(object sender, EventArgs e)
{
try
{
using (OpenFileDialog fileDialog = new OpenFileDialog()
{ Filter = "CSV|* .csv", ValidateNames = true, Multiselect = false })
if (fileDialog.ShowDialog() == DialogResult.OK)
{
String fileName = fileDialog.FileName;
filePath = fileName;
}
try
{
if (File.Exists(filePath))
{
lines = File.ReadAllLines(filePath).ToList();
foreach (String line in lines)
{
String addLine = line.Replace("'", "");
String addLine2 = addLine.Replace("\"", "");
String str = line.Substring(0, 1);
int num = int.Parse(str);
if (addLine2.Length > 1)
{
String addLine3 = addLine2.Substring(2);
switch (num)
{
case 2:
accountList.Add(addLine3);
break;
case 3:
statementList.Add(addLine3);
break;
case 4:
summaryList.Add(addLine3);
break;
case 5:
transactionList.Add(addLine3);
break;
}
}
}
}
else
{
MessageBox.Show("Invalid file chosen, choose an appropriate CSV file and try again.");
}
transactionLB.DataSource = transactionList;
//var liness = transactionList;
//foreach (string line in liness.Skip(2))
// transactionList2.Add(line);
//Console.WriteLine(transactionList2);
//var source = new BindingSource();
//source.DataSource = transactionList2;
//trans_dgv.DataSource = source;
accountLB.DataSource = accountList;
summaryLB.DataSource = summaryList;
statementLB.DataSource = statementList;
}
catch (Exception)
{
MessageBox.Show("Cannot load CSV file, Ensure that a valid CSV file is selected and try again.");
}
}
catch (Exception)
{
MessageBox.Show("Cannot open File Explorer, Something is wrong :(");
}
}
}
EDIT 1:
the table has the following columns for the transaction lists (each of the lists have different columns) :
'Number' , 'Date' , 'Description1' , 'Description2' , 'Description3' , 'Amount' , 'Balance' , 'Accrued Charges'
an example of data in the lines of the transaction list:
9, 02 Sep, Petrol Card Purchase, Shell Kempton Park, 968143*7188 30 Aug, -714.45, -10661.88, 5.5
some liness do contain null values.
If I understand correctly, it seems like you're wanting to get the value of the strings in your string list to appear in your DataGridViews. This can be a little tricky because the DataGridView needs to know which property to display and strings only have the Length property (which probably isn't what you're looking for). There are a lot of ways to go about getting the data you want into the DataGridView. For example you could use DataTables and choose which column you want displayed in the DataGridView. If you want to stick with using string lists, I think you could get this to work by modifying your DataSource line to look something like this:
transactionLB.DataSource = transactionList.Select(x => new { Value = x} ).ToList();
I hope this helps! Let me know if I've misunderstood your question. Thanks!
Im having an issue loading just 3 records from the text file into a datagridview.
private void button1_Click(object sender, EventArgs e)
{
using (OpenFileDialog ofd = new OpenFileDialog())
{
if (ofd.ShowDialog() == DialogResult.OK && radioButton1.Checked)
{
System.IO.StreamReader file = new System.IO.StreamReader(ofd.FileName);
string[] columnnames = file.ReadLine().Split('|');
List<string> list = new List<string>();
DataTable dt = new DataTable();
foreach (string c in columnnames)
{
dt.Columns.Add(c);
}
string newline;
while ((newline = file.ReadLine()) != null)
{
DataRow dr = dt.NewRow();
string[] values = newline.Split('|');
for (int i = 0; i < values.Length; i++)
{
dr[i] = values[i];
}
dt.Rows.Add(dr);
}
file.Close();
dataGridView1.DataSource = dt;
}
}
Im trying to have someone select a radio button such as "show 3 records" and open a text file. Then it would list the 3 records only in a datagridview. I can get the file to load the file but can't figure out how to make it only show 3 records from the text file. Could someone help me please?
Use File.ReadLines and Take
var records = File.ReadLines(ofd.FileName).Take(3);
foreach(var record in records)
{
// do stuff
}
The advantages of this approach, is under the hood ReadLines creates an iterator and calls the plumbing for StreamReader and reads each line individually. When combined with Take it only reads and loads what is iterated( in this case the first 3 lines).
You can find (and follow) the source code here
https://referencesource.microsoft.com/mscorlib/R/d989485a49fbbfd2.html
Additional Resources
File.ReadLines Method
Reads the lines of a file.
Enumerable.Take(IEnumerable, Int32) Method
Returns a specified number of contiguous elements from the start of a
sequence.
You need to count the number lines of read and then exit the read-load when it reaches 3 lines.
int maxLines = 3;
string newline;
while ((newline = file.ReadLine()) != null && --maxLines >= 0)
{
....
}
I am trying to develop a tool that will take a CSV file and import it into a datatable with the first column in the datatable being a row counter.
The CSV files are from different customers and so have different structures. Some have a header line; some have several header lines; some have no header line. They have also have varying columns.
So far, I have the code below.
public void Import_CSV()
{
OpenFileDialog dialog = new OpenFileDialog();
dialog.Filter = "CSV Files (*.csv)|*.csv";
bool? result = dialog.ShowDialog();
if (result ?? false)
{
string[] headers;
string CSVFilePathName = dialog.FileName;
string delimSelect = cboDelimiter.Items.GetItemAt(cboDelimiter.SelectedIndex).ToString();
// If user hasn't selected a delimiter, assume comma
if (delimSelect == "")
{
delimSelect = ",";
}
string[] delimiterType = new string[] {cboDelimiter.Items.GetItemAt(cboDelimiter.SelectedIndex).ToString()};
DataTable dt = new DataTable();
// Read first line of file to get number of fields and create columns and column numbers in data table
using (StreamReader sr1 = new StreamReader(CSVFilePathName))
{
headers = sr1.ReadLine().Split(delimiterType, StringSplitOptions.None);
//dt.Columns.Add("ROW", typeof(int));
//dt.Columns["ROW"].AutoIncrement = true;
//dt.Columns["ROW"].AutoIncrementSeed = 1;
//dt.Columns["ROW"].AutoIncrementStep = 1;
int colCount = 1;
foreach (string header in headers)
{
dt.Columns.Add("C" + colCount.ToString());
colCount++;
}
}
using (StreamReader sr = new StreamReader(CSVFilePathName))
{
while (!sr.EndOfStream)
{
string[] rows = sr.ReadLine().Split(delimiterType, StringSplitOptions.None);
DataRow dr = dt.NewRow();
for (int i = 0; i < headers.Length; i++)
{
dr[i] = rows[i];
}
dt.Rows.Add(dr);
}
}
dtGrid.ItemsSource = dt.DefaultView;
txtColCount.Text = dtGrid.Columns.Count.ToString();
txtRowCount.Text = dtGrid.Items.Count.ToString();
}
}
This works, in as much as it creates column headers (C1, C2....according to how many there are in the csv file) and then the rows are written in, but I want to add a column at the far left with a row number as the rows are added. In the code, you can see I've got a section commented out that creates an auto-number column, but I'm totally stuck on how the rows are written into the datatable. If I uncomment that section, I get errors as the first column in the csv file tries to write into an int field. I know you can specify which field in each row can go in which column, but that won't help here as the columns are unknown at this point. I just need it to be able to read ANY file in, regardless of the structure, but with the row counter.
Hope that makes sense.
You write in your question, that uncommenting the code that adds the first column leads to errors. This is because of your loop: it starts at 0, but the 0-th column is the one you have added manually. So you need just to skip it in your loop, starting at 1. However, the source array has to be processed from the 0-th element.
So the solution is:
First, uncomment the row adding code.
Then, in your loop, introduce an offset to leave the first column untouched:
for (int i = 0; i < headers.Length; i++)
{
dr[i + 1] = rows[i];
}
I'm trying to import a CSV file to my C# site and save it in the database. While doing research I learned about CSV parsing, I've tried to implement this but I've ran into some trouble. Here is a portion of my code so far:
string fileext = Path.GetExtension(fupcsv.PostedFile.FileName);
if (fileext == ".csv")
{
string csvPath = Server.MapPath("~/CSVFiles/") + Path.GetFileName(fupcsv.PostedFile.FileName);
fupcsv.SaveAs(csvPath);
// Add Columns to Datatable to bind data
DataTable dtCSV = new DataTable();
dtCSV.Columns.AddRange(new DataColumn[2] { new DataColumn("ModuleId", typeof(int)), new DataColumn("CourseId", typeof(int))});
// Read all the lines of the text file and close it.
string[] csvData = File.ReadAllLines(csvPath);
// iterate over each row and Split it to New line.
foreach (string row in csvData)
{
// Check for is null or empty row record
if (!string.IsNullOrEmpty(row))
{
using (TextFieldParser parser = new TextFieldParser(csvPath))
{
parser.TextFieldType = FieldType.Delimited;
parser.SetDelimiters(",");
while (!parser.EndOfData)
{
//Process row
string[] fields = parser.ReadFields();
int i = 1;
foreach (char cell in row)
{
dtCSV.NewRow()[i] = cell;
i++;
}
}
}
}
}
}
I keep getting the error "There is no row at position -1" at " dtCSV.Rows[dtCSV.Rows.Count - 1][i] = cell;"
Any help would be greatly appreciated, thanks
You are trying to index rows that you have not created. Instead of
dtCSV.Rows[dtCSV.Rows.Count - 1][i] = cell;
use
dtCSV.NewRow()[i] = cell;
I also suggest you start indexing i from 0 and not from 1.
All right so it turns out there were a bunch of errors with your code, so I made some edits.
string fileext = Path.GetExtension(fupcsv.PostedFile.FileName);
if (fileext == ".csv")
{
string csvPath = Server.MapPath("~/CSVFiles/") + Path.GetFileName(fupcsv.PostedFile.FileName);
fupcsv.SaveAs(csvPath);
DataTable dtCSV = new DataTable();
dtCSV.Columns.AddRange(new DataColumn[2] { new DataColumn("ModuleId", typeof(int)), new DataColumn("CourseId", typeof(int))});
var csvData = File.ReadAllLines(csvPath);
bool headersSkipped = false;
foreach (string line in csvData)
{
if (!headersSkipped)
{
headersSkipped = true;
continue;
}
// Check for is null or empty row record
if (!string.IsNullOrEmpty(line))
{
//Process row
int i = 0;
var row = dtCSV.NewRow();
foreach (var cell in line.Split(','))
{
row[i] = Int32.Parse(cell);
i++;
}
dtCSV.Rows.Add(row);
dtCSV.AcceptChanges();
}
}
}
I ditched the TextFieldParser solution solely because I'm not familiar with it, but if you want to stick with it, it shouldn't be hard to reintegrate it.
Here are some of the things you got wrong:
Not calling NewRow() to create a new row or adding it to the table with AddRow(row)
Iterating through the characters in row instead of the fields you parsed
Not parsing the value of cell - it's value type is string and you are trying to add to an int column
Some other things worth noting (just to improve your code's performance and readability :))
Consider using var when declaring new variables, it takes a lot of the stress away from having to worry about exactly what type of variable you are creating
As others in the comments said, use ReadAllLines() it parses your text file into lines neatly, making it easier to iterate through.
Most of the times when working with arrays or lists, you need to index from 0, not from 1
You have to use AcceptChanges() to commit all the changes you've made