I am trying to populate an ASP dropdownlist with a column from a CSV. I was able to do it with the below code, however, it shows duplicates since another one of the columns' values are all unique (so using .distinct() is no good). Here is an example of my CSV entries:
env,app,srvrole,servers
Dev,appname1,web,server01
Production,appname1,cache,server02
The servers column is always unique.
Here is my code:
var collection_of_objects =
(from line in File.ReadAllLines("\\server\\shares\\App\\Farms.csv").Skip(1)
let parts = line.Split(',')
select new
{
env = parts[0],
app = parts[1],
srvrole = parts[2],
servers = parts[3],
}
).ToList();
dropdown_app.DataSource = collection_of_objects.Distinct();
dropdown_app.DataTextField = "app";
dropdown_app.DataBind();
srvrole.DataSource = collection_of_objects.Distinct();
srvrole.DataTextField = "srvrole";
srvrole.DataBind();
I also attempted to use Streamreader and output to a Datatable, however, I could not assign the Datatable to the datasource of the dropdown. TYIA
I was able to figure it out using streamreader. If there are any easier ways to do this, I am all ears!
protected void Page_Load(object sender, EventArgs e)
{
List<string> environment = new List<String>();
List<string> application = new List<String>();
List<string> serverrole = new List<String>();
List<string> servers = new List<String>();
ReadCSV(environment, application, serverrole, servers);
string[] envlist = environment.ToArray();
string[] applist = application.ToArray();
string[] srvrolelist = serverrole.ToArray();
string[] serverlist = servers.ToArray();
dropdown_app.DataSource = applist.Skip(1).Distinct();
dropdown_app.DataBind();
srvrole.DataSource = srvrolelist.Skip(1).Distinct();
srvrole.DataBind();
}
public static void ReadCSV(List<string> environment, List<string> application, List<string> serverrole, List<string> servers)
{
StreamReader reader = new StreamReader(File.OpenRead(#"\\server\shares\App\Reference\Farms.csv"));
while (!reader.EndOfStream)
{
string line = reader.ReadLine();
if (!String.IsNullOrWhiteSpace(line))
{
string[] values = line.Split(',');
if (values.Length >= 4)
{
environment.Add(values[0]);
application.Add(values[1]);
serverrole.Add(values[2]);
servers.Add(values[3]);
}
}
}
}
Related
I am trying to read & use all lines from txt files. With a method I iterate through them asinc, and I try to get the data. My problem is, the output looks like it only contains the data from the first txt file. I just cant find where the problem is. I would appreciate any help.
Here's my code:
string[] files = Directory.GetFiles("C:/DPS-EDPWB05/forlogsearch", "*", SearchOption.AllDirectories);
//data I need from the txts
List<string> num = new List<string>();
List<string> date = new List<string>();
List<string> time = new List<string>();
List<string> sip = new List<string>();
List<string> csmethod = new List<string>();
List<string> csuristem = new List<string>();
List<string> csuriquery = new List<string>();
List<string> sport = new List<string>();
List<string> csusername = new List<string>();
List<string> cip = new List<string>();
List<string> csuseragent = new List<string>();
List<string> csreferer = new List<string>();
List<string> scstatus = new List<string>();
List<string> scsubstatus = new List<string>();
List<string> cswin32status = new List<string>();
List<string> timetaken = new List<string>();
int x = 0;
int y = 0;
int i = 0;
int filesCount = 0;
string v = "";
//Taking the data from the Log, getting a list of string[]
//items with the lines from the txts
List<string[]> lines = new List<string[]>();
while (i < files.Length)
{
lines.Add(ReadAllLinesAsync(files[i]).Result);
i++;
}
//Trying to get the data from the string[]s
do
{
string line;
int f = 0;
string[] linesOfTxt = lines[filesCount];
do
{
line = linesOfTxt[f];
string[] splittedLine = { };
splittedLine = line.Split(' ', 15, StringSplitOptions.None);
y = splittedLine.Count();
if (y == 15)
{
num.Add(x.ToString());
date.Add(splittedLine[0]);
time.Add(splittedLine[1]);
sip.Add(splittedLine[2]);
csmethod.Add(splittedLine[3]);
csuristem.Add(splittedLine[4]);
csuriquery.Add(splittedLine[5]);
sport.Add(splittedLine[6]);
csusername.Add(splittedLine[7]);
cip.Add(splittedLine[8]);
csuseragent.Add(splittedLine[9]);
csreferer.Add(splittedLine[10]);
scstatus.Add(splittedLine[11]);
scsubstatus.Add(splittedLine[12]);
cswin32status.Add(splittedLine[13]);
timetaken.Add(splittedLine[14]);
x++;
}
f++;
} while (f < linesOfTxt.Length);
filesCount++;
}
while (filesCount < files.Count());
After all this I group these and stuff but that happens AFTER the lists of data i need are filled - so the problem must be here somewhere. Also, my asinc reader (I found here on stackoverflow):
public static Task<string[]> ReadAllLinesAsync(string path)
{
return ReadAllLinesAsync(path, Encoding.UTF8);
}
public static async Task<string[]> ReadAllLinesAsync(string path, Encoding encoding)
{
var lines = new List<string>();
// Open the FileStream with the same FileMode, FileAccess
// and FileShare as a call to File.OpenText would've done.
using (var stream = new FileStream(path, FileMode.Open, FileAccess.Read, FileShare.Read, DefaultBufferSize, DefaultOptions))
using (var reader = new StreamReader(stream, encoding))
{
string line;
while ((line = await reader.ReadLineAsync()) != null)
{
lines.Add(line);
}
}
return lines.ToArray();
}
There are several problems with the example code, thou I'm unsure what is responsible for the actual issue.
Instead of writing your own ReadAllLinesAsync, just use File.ReadAllLinesAsync
You should in general avoid .Result since this is a blocking operation, and this has the potential to cause deadlocks. In this case you should just call the synchronous version, File.ReadAllLines, instead.
When possible, use foreach or for loops. They make it much easier to see if your code is correct, and avoids spreading the loop logic over multiple lines.
As far as I can see, you are processing each line identically, so you should be able to just merge all lines from all files by using List<string> lines and AddRange
Instead of keeping 15 different lists of properties, a more common approach would be to store one list of objects of a class with 15 different properties.
Whenever you need to store data, you should seriously consider using an existing serialization format, like json, xml, csv, protobuf etc. This lets you use existing, well tested, libraries for writing and reading data and converting it to your own types.
i am currently working on a small Project and i got stuck with a Problem i currently can not manage to solve...
I have multiple ".CSV" Files i want to read, they all have the same Data just with different Values.
Header1;Value1;Info1
Header2;Value2;Info2
Header3;Value3;Info3
While reading the first File i Need to Create the Headers. The Problem is they are not splited in Columns but in rows (as you can see above Header1-Header3).
Then it Needs to read the Value 1 - Value 3 (they are listed in the 2nd Column) and on top of that i Need to create another Header -> Header4 with the data of "Info2" which is always placed in Column 3 and Row 2 (the other values of Column 3 i can ignore).
So the Outcome after the first File should look like this:
Header1;Header2;Header3;Header4;
Value1;Value2;Value3;Info2;
And after multiple files it sohuld be like this:
Header1;Header2;Header3;Header4;
Value1;Value2;Value3;Value4;
Value1b;Value2b;Value3b;Value4b;
Value1c;Value2c;Value3c;Value4c;
I tried it with OleDB but i get the Error "missing ISAM" which i cant mange to fix. The Code i Used is the following:
public DataTable ReadCsv(string fileName)
{
DataTable dt = new DataTable("Data");
/* using (OleDbConnection cn = new OleDbConnection("Provider=Microsoft.Jet.OLEDB.4.0;Data Source=\"" +
Path.GetDirectoryName(fileName) + "\";Extendet Properties ='text;HDR=yes;FMT=Delimited(,)';"))
*/
using (OleDbConnection cn = new OleDbConnection("Provider=Microsoft.Jet.OLEDB.4.0;Data Source=" +
Path.GetDirectoryName(fileName) + ";Extendet Properties ='text;HDR=yes;FMT=Delimited(,)';"))
{
using(OleDbCommand cmd = new OleDbCommand(string.Format("select *from [{0}]", new FileInfo(fileName).Name,cn)))
{
cn.Open();
using(OleDbDataAdapter adapter = new OleDbDataAdapter(cmd))
{
adapter.Fill(dt);
}
}
}
return dt;
}
Another attempt i did was using StreamReader. But the Headers are in the wrong place and i dont know how to Change this + do this for every file. the Code i tried is the following:
public static DataTable ReadCsvFilee(string path)
{
DataTable oDataTable = new DataTable();
var fileNames = Directory.GetFiles(path);
foreach (var fileName in fileNames)
{
//initialising a StreamReader type variable and will pass the file location
StreamReader oStreamReader = new StreamReader(fileName);
// CONTROLS WHETHER WE SKIP A ROW OR NOT
int RowCount = 0;
// CONTROLS WHETHER WE CREATE COLUMNS OR NOT
bool hasColumns = false;
string[] ColumnNames = null;
string[] oStreamDataValues = null;
//using while loop read the stream data till end
while (!oStreamReader.EndOfStream)
{
String oStreamRowData = oStreamReader.ReadLine().Trim();
if (oStreamRowData.Length > 0)
{
oStreamDataValues = oStreamRowData.Split(';');
//Bcoz the first row contains column names, we will poluate
//the column name by
//reading the first row and RowCount-0 will be true only once
// CHANGE TO CHECK FOR COLUMNS CREATED
if (!hasColumns)
{
ColumnNames = oStreamRowData.Split(';');
//using foreach looping through all the column names
foreach (string csvcolumn in ColumnNames)
{
DataColumn oDataColumn = new DataColumn(csvcolumn.ToUpper(), typeof(string));
//setting the default value of empty.string to newly created column
oDataColumn.DefaultValue = string.Empty;
//adding the newly created column to the table
oDataTable.Columns.Add(oDataColumn);
}
// SET COLUMNS CREATED
hasColumns = true;
// SET RowCount TO 0 SO WE KNOW TO SKIP COLUMNS LINE
RowCount = 0;
}
else
{
// IF RowCount IS 0 THEN SKIP COLUMN LINE
if (RowCount++ == 0) continue;
//creates a new DataRow with the same schema as of the oDataTable
DataRow oDataRow = oDataTable.NewRow();
//using foreach looping through all the column names
for (int i = 0; i < ColumnNames.Length; i++)
{
oDataRow[ColumnNames[i]] = oStreamDataValues[i] == null ? string.Empty : oStreamDataValues[i].ToString();
}
//adding the newly created row with data to the oDataTable
oDataTable.Rows.Add(oDataRow);
}
}
}
//close the oStreamReader object
oStreamReader.Close();
//release all the resources used by the oStreamReader object
oStreamReader.Dispose();
}
return oDataTable;
}
I am thankful for everyone who is willing to help. And Thanks for reading this far!
Sincerely yours
If I understood you right, there is a strict parsing there like this:
string OpenAndParse(string filename, bool firstFile=false)
{
var lines = File.ReadAllLines(filename);
var parsed = lines.Select(l => l.Split(';')).ToArray();
var header = $"{parsed[0][0]};{parsed[1][0]};{parsed[2][0]};{parsed[1][0]}\n";
var data = $"{parsed[0][1]};{parsed[1][1]};{parsed[2][1]};{parsed[1][2]}\n";
return firstFile
? $"{header}{data}"
: $"{data}";
}
Where it would return - if first file:
Header1;Header2;Header3;Header2
Value1;Value2;Value3;Value4
if not first file:
Value1;Value2;Value3;Value4
If I am correct, rest is about running this against a list file of files and joining the results in an output file.
EDIT: Against a directory:
void ProcessFiles(string folderName, string outputFileName)
{
bool firstFile = true;
foreach (var f in Directory.GetFiles(folderName))
{
File.AppendAllText(outputFileName, OpenAndParse(f, firstFile));
firstFile = false;
}
}
Note: I missed you want a DataTable and not an output file. Then you could simply create a list and put the results into that list making the list the datasource for your datatable (then why would you use semicolons in there? Probably all you need is to simply attach the array values to a list).
(Adding as another answer just to make it uncluttered)
void ProcessMyFiles(string folderName)
{
List<MyData> d = new List<MyData>();
var files = Directory.GetFiles(folderName);
foreach (var file in files)
{
OpenAndParse(file, d);
}
string[] headers = GetHeaders(files[0]);
DataGridView dgv = new DataGridView {Dock=DockStyle.Fill};
dgv.DataSource = d;
dgv.ColumnAdded += (sender, e) => {e.Column.HeaderText = headers[e.Column.Index];};
Form f = new Form();
f.Controls.Add(dgv);
f.Show();
}
string[] GetHeaders(string filename)
{
var lines = File.ReadAllLines(filename);
var parsed = lines.Select(l => l.Split(';')).ToArray();
return new string[] { parsed[0][0], parsed[1][0], parsed[2][0], parsed[1][0] };
}
void OpenAndParse(string filename, List<MyData> d)
{
var lines = File.ReadAllLines(filename);
var parsed = lines.Select(l => l.Split(';')).ToArray();
var data = new MyData
{
Col1 = parsed[0][1],
Col2 = parsed[1][1],
Col3 = parsed[2][1],
Col4 = parsed[1][2]
};
d.Add(data);
}
public class MyData
{
public string Col1 { get; set; }
public string Col2 { get; set; }
public string Col3 { get; set; }
public string Col4 { get; set; }
}
I don't know if this is the best way to do this. But what i would have done in your case, is to rewrite the CSV's the conventionnal way while reading all the files, then create a stream containing the new CSV created.
It would look like something like this :
var csv = new StringBuilder();
csv.AppendLine("Header1;Header2;Header3;Header4");
foreach (var item in file)
{
var newLine = string.Format("{0},{1},{2},{3}", item.value1, item.value2, item.value3, item.value4);
csv.AppendLine(newLine);
}
//Create Stream
MemoryStream stream = new MemoryStream();
StreamReader reader = new StreamReader(stream);
//Fill your data table here with your values
Hope this will help.
i have to import 2 CSV's.
CSV 1 [49]: Including about 50 tab seperated colums.
CSV 2:[2] Inlcudes 3 Columns which should be replaced on the [3] [6] and [11] place of my first csv.
So heres what i do:
1) Importing the csv and split into a array.
string employeedatabase = "MYPATH";
List<String> status = new List<String>();
StreamReader file2 = new System.IO.StreamReader(filename);
string line = file2.ReadLine();
while ((line = file2.ReadLine()) != null)
{
string[] ud = line.Split('\t');
status.Add(ud[0]);
}
String[] ud_status = status.ToArray();
PROBLEM 1: i have about 50 colums to handle, ud_status is just the first, so do i need 50 Lists and 50 String arrays?
2) Importing the second csv and split into a array.
List<String> vorname = new List<String>();
List<String> nachname = new List<String>();
List<String> username = new List<String>();
StreamReader file = new System.IO.StreamReader(employeedatabase);
string line3 = file.ReadLine();
while ((line3 = file.ReadLine()) != null)
{
string[] data = line3.Split(';');
vorname.Add(data[0]);
nachname.Add(data[1]);
username.Add(data[2]);
}
String[] db_vorname = vorname.ToArray();
String[] db_nachname = nachname.ToArray();
String[] db_username = username.ToArray();
PROBLEM 2: After loading these two csv's i dont know how to combine them, and change to columns as mentioned above ..
somethine like this?
mynewArray = ud_status + "/t" + ud_xy[..n] + "/t" + changed_colum + ud_xy[..n];
save "mynewarray" into tablulator seperated csv with encoding "utf-8".
To read the file into a meaningful format, you should set up a class that defines the format of your CSV:
public class CsvRow
{
public string vorname { get; set; }
public string nachname { get; set; }
public string username { get; set; }
public CsvRow (string[] data)
{
vorname = data[0];
nachname = data[1];
username = data[2];
}
}
Then populate a list of this:
List<CsvRow> rows = new List<CsvRow>();
StreamReader file = new System.IO.StreamReader(employeedatabase);
string line3 = file.ReadLine();
while ((line3 = file.ReadLine()) != null)
{
rows.Add(new CsvRow(line3.Split(';'));
}
Similarly format your other CSV and include unused properties for the new fields. Once you have loaded both, you can populate the new properties from this list in a loop, matching the records by whatever common field the CSVs hopefully share. Then finally output the resulting data to a new CSV file.
Your solution is not to use string arrays to do this. That will just drive you crazy. It's better to use the System.Data.DataTable object.
I didn't get a chance to test the LINQ lambda expression at the end of this (or really any of it, I wrote this on a break), but it should get you on the right track.
using (var ds = new System.Data.DataSet("My Data"))
{
ds.Tables.Add("File0");
ds.Tables.Add("File1");
string[] line;
using (var reader = new System.IO.StreamReader("FirstFile"))
{
//first we get columns for table 0
foreach (string s in reader.ReadLine().Split('\t'))
ds.Tables["File0"].Columns.Add(s);
while ((line = reader.ReadLine().Split('\t')) != null)
{
//and now the rest of the data.
var r = ds.Tables["File0"].NewRow();
for (int i = 0; i <= line.Length; i++)
{
r[i] = line[i];
}
ds.Tables["File0"].Rows.Add(r);
}
}
//we could probably do these in a loop or a second method,
//but you may want subtle differences, so for now we just do it the same way
//for file1
using (var reader2 = new System.IO.StreamReader("SecondFile"))
{
foreach (string s in reader2.ReadLine().Split('\t'))
ds.Tables["File1"].Columns.Add(s);
while ((line = reader2.ReadLine().Split('\t')) != null)
{
//and now the rest of the data.
var r = ds.Tables["File1"].NewRow();
for (int i = 0; i <= line.Length; i++)
{
r[i] = line[i];
}
ds.Tables["File1"].Rows.Add(r);
}
}
//you now have these in functioning datatables. Because we named columns,
//you can call them by name specifically, or by index, to replace in the first datatable.
string[] columnsToReplace = new string[] { "firstColumnName", "SecondColumnName", "ThirdColumnName" };
for(int i = 0; i < ds.Tables[0].Rows.Count; i++)
{
//you didn't give a sign of any relation between the two tables
//so this is just by row, and assumes the row count is equivalent.
//This is also not advised.
//if there is a key these sets of data share
//you should join on them instead.
foreach(DataRow dr in ds.Tables[0].Rows[i].ItemArray)
{
dr[3] = ds.Tables[1].Rows[i][columnsToReplace[0]];
dr[6] = ds.Tables[1].Rows[i][columnsToReplace[1]];
dr[11] = ds.Tables[1].Rows[i][columnsToReplace[2]];
}
}
//ds.Tables[0] now has the output you want.
string output = String.Empty;
foreach (var s in ds.Tables[0].Columns)
output = String.Concat(output, s ,"\t");
output = String.Concat(output, Environment.NewLine); // columns ready, now the rows.
foreach (DataRow r in ds.Tables[0].Rows)
output = string.Concat(output, r.ItemArray.SelectMany(t => (t.ToString() + "\t")), Environment.NewLine);
if(System.IO.File.Exists("MYPATH"))
using (System.IO.StreamWriter file = new System.IO.StreamWriter("MYPATH")) //or a variable instead of string literal
{
file.Write(output);
}
}
With Cinchoo ETL - an open source file helper library, you can do the merge of CSV files as below. Assumed the 2 CSV file contains same number of lines.
string CSV1 = #"Id Name City
1 Tom New York
2 Mark FairFax";
string CSV2 = #"Id City
1 Las Vegas
2 Dallas";
dynamic rec1 = null;
dynamic rec2 = null;
StringBuilder csv3 = new StringBuilder();
using (var csvOut = new ChoCSVWriter(new StringWriter(csv3))
.WithFirstLineHeader()
.WithDelimiter("\t")
)
{
using (var csv1 = new ChoCSVReader(new StringReader(CSV1))
.WithFirstLineHeader()
.WithDelimiter("\t")
)
{
using (var csv2 = new ChoCSVReader(new StringReader(CSV2))
.WithFirstLineHeader()
.WithDelimiter("\t")
)
{
while ((rec1 = csv1.Read()) != null && (rec2 = csv2.Read()) != null)
{
rec1.City = rec2.City;
csvOut.Write(rec1);
}
}
}
}
Console.WriteLine(csv3.ToString());
Hope it helps.
Disclaimer: I'm the author of this library.
I want to read php text file using c#. The file looks like:
2.20:2.20:2.20:2.20:2.20:
2012-07-12:2012-07-11:2012-07-10:2012-07-09:2012-07-08:
I would like to get all lines to listboxes. In real situation there is six lines, but first I should have read these two lines. My code:
void web_OpenReadCompleted(object sender, OpenReadCompletedEventArgs e)
{
try
{
int i;
string price_line = "";
string date_line = "";
List<decimal> prices = new List<decimal>();
List<string> dates = new List<string>();
using (var reader = new StreamReader(e.Result))
{
price_line = reader.ReadLine();
date_line = reader.ReadLine();
string[] bit_1 = price_line.Split(':');
string[] bit_2 = date_line.Split(':');
for (i = 0; i < 2; i++)
{
prices.Add(decimal.Parse(bit_1[i]));
dates.Add(bit_2[i]);
}
listBox1.ItemsSource = prices;
listBox2.ItemsSource = dates;
}
}
catch
{
MessageBox.Show("Can't read!");
}
}
Now I get "NullException". How to fix this?
EDIT:
What's about:
using (StreamReader reader = new StreamReader(e.Result))
{
List<string> lines = new List<string>();
while (!reader.EndOfStream)
lines.Add(reader.ReadLine());
string prices = lines.First().Split(':');
List<decimal> listPrices = new List<decimal>();
List<string> listDates = lines.Last().Split(':').ToList();
foreach(string s in prices)
listPrices.Add(double.Parse(s));
listBox1.ItemsSource = listPrices;
listBox2.ItemsSource = listDates;
}
You should check if e.Result, listBox1 and listBox2 aren't null.
I'm reading a local csv file which has data and I will eventually use to load into a database. My question is simple in nature but I can't seem to grasp the concept. Below is my code. Pretty simple stuff.
static void loadTables() {
int size = new int();
string[] empid = new string[0];
//string[] empid = new string();
List<string[]> EmployeeName = new List<string[]>();
List<string[]> EmployeeId = new List<string[]>();
List<string[]> Group = new List<string[]>();
List<string[]> Org = new List<string[]>();
List<string[]> Fund = new List<string[]>();
try {
using (StreamReader readFile = new StreamReader("C:\\temp\\groupFundOrgReport.csv"))
{
string line;
string[] row;
size = 0;
while ((line = readFile.ReadLine()) != null)
{
row = line.Split(',');
/*resize the array up by 1*/
Array.Resize(ref empid,empid.Length+1);
/*I'm using size as a counter to add to the slot on the array.*/
empid[size] = row[0].Remove(0,1);
// here I receive error (the best overload match of system generic list?...etc)
EmployeeName.Add(row[0]);
size++;
}
}
}
catch(Exception e){
Console.WriteLine(e);
}
}
I have a list of string but any attempts to add a string to it gets me an error. In other words if I try to do this EmployeeName.Add(row[0].ToString); I get an error. However if I comment the line out I can use an old fashion array. I really like to use a list but I can't seem to be able to pass the value that I want. Can someone please point me in the right direction?
I guess from your code that the employee name is the first field of the CSV file.
You have declared EmployeeName as a List of arrays of strings List<string[]>, not as a list of strings List<string>.
Row[0] is the first string in an array, so you are trying to add a string to a list that is expecting you to add an array of strings.
You should just declare EmployeeName as a List<string>, using a line like:
List<string> EmployeeName = new List<string>();
or
var EmployeeName = new List<string>();
Your problem is the declaration of EmployeeName, it is a List of string arrays:
List<string[]> EmployeeName = new List<string[]>();
Change it to:
var EmployeeName = new List<string>();
Or, use the List<string[]> accordingly ...
EmployeeName is a List<string[]> so you have to write:
EmployeeName.Add(row);
To remove empty entries while splitting a string use:
row=line.Split(New String() {","},
StringSplitOptions.RemoveEmptyEntries);
All of them are List's of String Array's
List<string[]> EmployeeName = new List<string[]>();
List<string[]> EmployeeId = new List<string[]>();
List<string[]> Group = new List<string[]>();
List<string[]> Org = new List<string[]>();
List<string[]> Fund = new List<string[]>();
Only variable you can add would be like
//Define array of strings
var strArr = new string[] {"abc", "xyz"};
then you can call
EmployeeName.Add(strArr);
although changing the List generic type to string type will solve your problem
List<string> EmployeeName = new List<string>();
List<string> EmployeeId = new List<string>();
List<string> Group = new List<string>();
List<string> Org = new List<string>();
List<string> Fund = new List<string>();
var str = "My String";
EmployeeName.Add(str);