Merge 2 lines in .CSV file using StreamReader - c#

I am currently trying to merge some lines in a .csv file. The file follows a specific format which is split by "," and the last element uses \n ascii code. This means the last element gets put onto a new line and i return an array with only one Element. I am looking to merge this element with the line above it.
So my line would be:
192.168.60.24, ACD_test1,86.33352, 07/12/2014 13:33:13, False, Annotated, True,"Attribute1
Attribute 2
Attribute 3"
192.168.60.24, ACD_test1,87.33352, 07/12/2014 13:33:13, False, Annotated, True
Is it possible to merge/join the new line attributes with the line above?
My code is shown below:
var reader = new StreamReader(File.OpenRead(#path));
string line1 = reader.ReadLine();
if (line1.Contains("Server, Tagname, Value, Timestamp, Questionable, Annotated, Substituted"))
{
while (!reader.EndOfStream)
{
List<string> listPointValue = new List<string>();
var line = reader.ReadLine();
var values = line.Split(',');
if (values.Count() < 2)
{
//*****Trying to Add Attribute to listPointValue.ElememtAt(0) here******
}
else
{
foreach (string value in values)
{
listPointValue.Add(value);
}
allValues.Add(listPointValue);
}
}
// allValues.RemoveAt(0);
return allValues;
}

I think you want to read the next line before you do the allValues.Add. That way you can decide whether to add the previous line to allValues (starting a new line). This gives you an idea of what I mean:
var reader = new StreamReader(File.OpenRead(#path));
string line1 = reader.ReadLine();
if (line1.Contains("Server, Tagname, Value, Timestamp, Questionable, Annotated, Substituted"))
{
List<string> listPointValue = new List<string>();
// Add first line to listPointValue
var line = reader.ReadLine();
var values = line.Split(',');
foreach (string value in values)
{
listPointValue.Add(value);
}
while (!reader.EndOfStream)
{
// Read next line
line = reader.ReadLine();
values = line.Split(',');
// If next line is a full line, add the previous line and create a new line
if (values.Count() > 1)
{
allValues.Add(listPointValue);
listPointValue = new List<string>();
}
// Add values to line
foreach (string value in values)
{
listPointValue.Add(value);
}
}
allValues.Add(listPointValue);
}

Related

searching a line from a text with a keyword (solved) and display the third line after the searched line

i want to display the third line after searhing the initial line with a keyword from a text :
and i want to sperate all the variable in the third line in textboxs.
the keyword is [Ref 1]
{ // string motcledm = "code:A14";
string line;
string motcletest = SEARCH.Text;
using (System.IO.StreamReader file = new System.IO.StreamReader(#"D:\\TEST.txt"))
{
while ((line = file.ReadLine()) != null)
{
if ((line.Contains(motcletest)))
{
richTextBox1.Text = line.ToString();
}
}
The output i need
As you told, you have extracted the Third Line, Split on ',' to get an array of strings
String thirdLine = "F8,F9,...";
String[] strArray = thirdLine.Split(',');
foreach(string _val in strArray){
//do your stuff
}
string line;
string motcletest = SEARCH.Text;
using (System.IO.StreamReader file = new System.IO.StreamReader(#"D:\\TEST.txt"))
{
while ((line = file.ReadLine()) != null)
{
if ((line.Contains(motcletest)))
{
richTextBox1.Text = line.ToString();
file.ReadLine();//read first line after matching line
file.ReadLine();//read second line after matching line
line = file.ReadLine(); //third line that you are looking for
foreach(var value in line.Split(','))//split by ,
{
//Add the value the controls(textbox)
//if the count is not fixed, you might need to create a control and add it to a panel
}
}
}

Read csv multi column & export it

my csv data is something looks like this:
Device data for period 30/08/2016 to 30/08/2016
Site ID,Time,INC1_MD
VSI-18,2016-08-30 00:00:00,165.954
VSI-18,2016-08-30 00:01:00,14.524
VSI-18,2016-08-30 00:02:00,32.920
VSI-18,2016-08-30 00:03:00,48.508
VSI-18,2016-08-30 00:04:00,62.418
.....
and I try to ignore first two line and start at "VSI-18..."
and extract third column data which is after the date & time column
and export them into new csv file, 1 column per day
like:
day1,day2,day3
100,200,300
200,123,123
123,222,444
....
and here is my code
o_csv_loc.Text = varFile; //csv data file location
save_file_loc.Text = saveloc; //new csv file location
var reader = new StreamReader(File.OpenRead(varFile));
List <string[]> listA = new List<string[]>();
List<string[]> listB = new List<string[]>();
List<string[]> listC = new List<string[]>();
//I think these two code below is to skip first 2 line of csv data
//file and start read the third line (VSI-18...)
reader.ReadLine();
reader.ReadLine();
while (reader.Peek() > -1)
{
var line = reader.ReadLine();
var values = line.Split(';');
listA.Add(new string[] { values[0] });
listB.Add(new string[] { values[1] });
listC.Add(new string[] { values[2] });
//I think that listC is suppose to extract the data after the
//second comma which is third column
}
for the export data code I not yet finish because I can't figure out how to read data yet.
when debug, 'System.IndexOutOfRangeException' show on line
listB.Add(new string[] { values[1] });
Isn't should not be problem on this line? values[0] is not problem yet.
EDIT
I success to export data to new csv file
var reader = new StreamReader(File.OpenRead(varFile));
List <string[]> listA = new List<string[]>(); //here are the code
//changed
List<string[]> listB = new List<string[]>();
List<string[]> listC = new List<string[]>();
reader.ReadLine();
reader.ReadLine();
while (reader.Peek() > -1)
{
var line = reader.ReadLine();
var values = line.Split(',');
listA.Add(new string[] { values[0] });
listB.Add(new string[] { values[1] });
listC.Add(new string[] { values[2]});
}
using (System.IO.TextWriter writer = File.CreateText(saveloc))
{
for (int index = 0; index < listC.Count; index++)
{
writer.WriteLine(string.Join(",", listC[index]) + ',');
}
}
result is this:
165.954,
14.524,
32.920,
48.508,
62.418,
79.151,
96.982,
I still figuring how to detect new date and put into new column
First, like one of the comments stated...in your code you're using ; as the delimiter character however the csv file is using ,...so the result of var values = line.Split(';'); is an aray with only one element.
Second, I would safeguard my application against incorrect formats or corrupted data. For example
var line = reader.ReadLine();
if(string.IsNullOrEmpty())//<--empty row
continue;//<--ignore, or else add empty values to your in-memory lists
var values = line.Split(',');
listA.Add(new string[] { values.length > 0 ? values[0] : string.Empty });
listB.Add(new string[] { values.length > 1 ? values[1] : string.Empty });
listC.Add(new string[] { values.length > 2 ? values[2] : string.Empty });
//or simply
if(values.length < 3)
continue;//<--ignore, or else add empty values to your in-memory lists
Few points.
You might be getting an error/exception due to delimiter (;) used doesn't actually split the string, so values[1] throws IndexOutOfRange exception . Use the correct delimiters (, what you need).
If your intention is to generate new csv (with same string) why do you need to split the string? can't we directly write it to file (assuming it is , delimited)?
var sb = new StringBuilder();
while (reader.Peek() > -1)
{
var line = reader.ReadLine();
sb.AppendLine(line);
}
// Write to file.
File.WriteAllText(filePath, sb.ToString());

Effective way of reading text file in c# for line to line operation

I'm having problem in reading text file for line to line addition operation. I have used following syntax
StreamReader reader = new StreamReader("input.txt");
string line;
while ((line = reader.ReadLine()) != null)
string[] splitted = line.Split('#');
string first = splitted[0].Trim();
string second = splitted[1].Trim();
I have used this syntax to separate the input from text file if file has following values.
12#15
15#7
13#14
23#31
x= Convert.ToInt32(first);
y= Convert.ToInt32(second);
sum = x+y;
txtBox.text = Convert.ToString(sum);
the problem is it only executes the last line. It only calculate the sum of 23 and 31 and show only but I want to add 12 and 15 first and show it in textbox similarly I want to add others. please help me in forming appropriate syntax.
The question is vague one, however, I suggest using Linq:
var source = File
.ReadLines("input.txt") // read line by line
.Select(line => line.Split('#')) // split each line
//.Where(items => items.Length == 2) // you may want to filter out the lines
.Select(items => new { // convert each line into anonymous class
first = items[0].Trim(),
second = items[1].Trim()
});
You can add as many Select (line to line opetations) as you want. Then you can proceed the items in a foreach loop:
foreach (var item in source) {
...
// Let's read some fields from the anonymous object
var first = item.first;
var second = item.second;
...
}
Edit: according to the edited question you want just to sum up which can be done via Linq as well:
var result = File
.ReadLines("input.txt")
.Select(line => line.Split('#'))
//.Where(items => items.Length == 2) // you may want to filter out the lines
.Sum(items => int.Parse(items[0]) + int.Parse(items[1]));
txtBox.text = result.ToString();
It doesn't only read the last line, you just never do anything with the other iterations. Currently you just keep reassigning line with the value of the latest line that has been read, I presume you hope to save these to a list or similar
StreamReader reader = new StreamReader("input.txt");
string line;
List<string> allLines = new List<string>();
while ((line = reader.ReadLine()) != null)
allLines.Add(line);
here you can test your File and Load The data into a DataTable this should be pretty straight forward.
DataTable dtTextFileData = new DataTable();
dtTextFileData.Columns.AddRange(new []
{
new DataColumn("First", typeof(string)),
new DataColumn("Second", typeof(string))
});
StreamReader file = new StreamReader(#"c:\YourFilePath\input.txt");
string line = file.ReadLine();
while (line != null)
{
string[] fields = line.Split('#');
DataRow dr = dtTextFileData.NewRow();
dr["First"] = fields[0].ToString();
dr["Second"] = fields[1].ToString();
dtTextFileData.Rows.Add(dr);
line = file.ReadLine();
}

Copying CSV file while reordering/adding empty columns

Copying CSV file while reordering/adding empty columns.
For example if ever line of incoming file has values for 3 out of 10 columns in order different from output like (except first which is header with column names):
col2,col6,col4 // first line - column names
2, 5, 8 // subsequent lines - values for 3 columns
and output expected to have
col0,col1,col2,col3,col4,col5,col6,col7,col8,col9
then output should be "" for col0,col1,col3,col5,col7,col8,col9,and values from col2,col4,col4 in the input file. So for the shown second line (2,5,8) expected output is ",,2,,5,,8,,,,,"
Below code I've tried and it is slower than I want.
I have two lists.
The first list filecolumnnames is created by splitting a delimited string (line) and this list gets recreated for every line in the file.
The second list list has the order in which the first list needs to be rearranged and re concatenated.
This works
string fileName = "F:\\temp.csv";
//file data has first row col3,col2,col1,col0;
//second row: 4,3,2,1
//so on
string fileName_recreated = "F:\\temp_1.csv";
int count = 0;
const Int32 BufferSize = 1028;
using (var fileStream = File.OpenRead(fileName))
using (var streamReader = new StreamReader(fileStream, Encoding.UTF8, true, BufferSize))
{
String line;
List<int> list = new List<int>();
string orderedcolumns = "\"\"";
string tableheader = "col0,col1,col2,col3,col4,col5,col6,col7,col8,col9,col10";
List<string> tablecolumnnames = new List<string>();
List<string> filecolumnnames = new List<string>();
while ((line = streamReader.ReadLine()) != null)
{
count = count + 1;
StringBuilder sb = new StringBuilder("");
tablecolumnnames = tableheader.Split(',').ToList();
if (count == 1)
{
string fileheader = line;
//fileheader=""col2,col1,col0"
filecolumnnames = fileheader.Split(',').ToList();
foreach (string col in tablecolumnnames)
{
int index = filecolumnnames.IndexOf(col);
if (index == -1)
{
sb.Append(",");
// orderedcolumns=orderedcolumns+"+\",\"";
list.Add(-1);
}
else
{
sb.Append(filecolumnnames[index] + ",");
//orderedcolumns = orderedcolumns+ "+filecolumnnames["+index+"]" + "+\",\"";
list.Add(index);
}
// MessageBox.Show(orderedcolumns);
}
}
else
{
filecolumnnames = line.Split(',').ToList();
foreach (int items in list)
{
//MessageBox.Show(items.ToString());
if (items == -1)
{
sb.Append(",");
}
else
{
sb.Append(filecolumnnames[items] + ",");
}
}
//expected format sb.Append(filecolumnnames[3] + "," + filecolumnnames[2] + "," + filecolumnnames[2] + ",");
//sb.Append(orderedcolumns);
var result = String.Join (", ", list.Select(index => filecolumnnames[index]));
}
using (FileStream fs = new FileStream(fileName_recreated, FileMode.Append, FileAccess.Write))
using (StreamWriter sw = new StreamWriter(fs))
{
sw.WriteLine(sb.ToString());
}
}
I am trying to make it faster by constructing a string orderedcolumns and remove the second for each loop which happens for every row and replace it with constructed string.
so if you uncomment the orderedcolumns string construction orderedcolumns = orderedcolumns+ "+filecolumnnames["+index+"]" + "+\",\""; and uncomment the append sb.Append(orderedcolumns); I am expecting the value inside the constructed string but when I append the orderedcolumns it is appending the text i.e.
""+","+filecolumnnames[3]+","+filecolumnnames[2]+","+filecolumnnames[1]+","+filecolumnnames[0]+","+","+","+","+","+","+","
i.e. I instead want it to take the value inside the filecolumnnames[3] list and not the filecolumnnames[3] name itself.
Expected value: if that line has 1,2,3,4
I want the output to be 4,3,2,1 as filecolumnnames[3] will have 4, filecolumnnames[2] will have 3..
String.Join is the way to construct comma/space delimited strings from sequence.
var result = String.Join (", ", list.Select(index => filecolumnnames[index]);
Since you are reading only subset of columns and orders in input and output don't match I'd use dictionary to hold each row of input.
var row = tablecolumnnames
.Zip(line.Split(','), (Name,Value)=> new {Name,Value})
.ToDictionary(x => x.Name, x.Value);
For output I'd fill sequence from defaults or input row:
var outputLine = String.Join(",",
filecolumnnames
.Select(name => row.ContainsKey(name) ? row[name] : ""));
Note code is typed in and not compiled.
orderedcolumns = orderedcolumns+ "+filecolumnnames["+index+"]" + "+\",\""; "
should be
orderedcolumns = orderedcolumns+ filecolumnnames[index] + ",";
you should however use join as others have pointed out. Or
orderedcolumns.AppendFormat("{0},", filecolumnnames[index]);
you will have to deal with the extra ',' on the end

C# text file deduping based on split

what i want to do is de-dupe a text file (against itself) based on the split. Once the de-dupe has been complete write out to a new file and keep the first result. So a basic example is. I guess the question is how do you de dupe a text file in C# based on a string split.
File 1:
Apple|Turnip3234
Apple|Tunip22
Fox|dsa34
Turtle|3423
Hamster|d34
Fox|sdw2
Result:
Apple|Turnip3234
Fox|dsa34
Turtle|3423
Hamster|d34
string inputFile; // = ...
string outputFile; // = ...
HashSet<string> keys = new HashSet<string>();
using (StreamReader reader = new StreamReader(inputFile))
using (StreamWriter writer = new StreamWriter(outputFile))
{
string line = reader.ReadLine();
while (line != null)
{
string candidate = line.Split('|')[0];
if (keys.Add(candidate))
writer.WriteLine(line);
line = reader.ReadLine();
}
}
Use HashSet<string>. Store there left part of line (everything preceding |).
On each line call hashset.Contains(leftpart) to test if that line is a "dupe".
You can create Dictionary<string,string> where key is your first word and value is the second one. Then you can just go through all your lines, split them and check if first word occurs in Keys, and add this pair if it does not.
This will always use the first value encountered (and it's untested, but the concepts are correct).
Dictionary<String, String> dupeMap = new Dictionary<String, String>();
foreach (string line in File.Readlines("foo.txt")) {
key = line.Split("|")[0];
if (!dupeMap.ContainsKey(key)) {
dupeMap.Add(key, line);
}
}
Then you can write them all back by iterating over the Dictionary, though this is not stable because you can't be certain to get the lines back in order.
using (TextWriter tw = new StreamWriter("foo.txt")) {
foreach (string key in dupeMap.Keys()) {
tw.WriteLine(dupeMap[key]);
}
}
An easy solution is to only add values you haven't met yet.
var allLines = File.ReadAllLines(#"c:\test.txt");
Dictionary<string, string> allUniques = new Dictionary<string, string>();
foreach(string s in allLines)
{
var chunks = s.Split('|');
if (!allUniques.ContainsKey(chunks[0]))
{
allUniques.Add(chunks[0], s);
}
}
File.WriteAllLines(#"c:\test2.txt", allUniques.Values.ToArray());

Categories