I have a program that checks two text files for a specific field then checks to see if either file has the specified field. If it does then the number of matches is stored into another List. The problem I am having is that it is only writing the first match to the text file, when I know I have two matches. I am fairly new to C# so any help/advice would be appreciated, the code below is doing the check.
while ((lineBeingRead = fileToRead.ReadLine()) != null)
{
if (lineBeingRead.IndexOf(" :22:", 0) == 0)
{
lstTwentyOneCounter.Add(lineBeingRead.Substring(11));
lstStoreTwentyOne = lstTwentyOneCounter;
}
}
The code below is writing to the text file.
foreach (var single103 in lstStore103)
{
foreach (var single101 in lstStore101)
{
if (single101 == single103)
{
checkResults.Add(single103);
System.IO.File.WriteAllText(#"H:\Compare.txt", single103);
break;
}
}
}
Thanks,
Ryan
WriteAllText will overwrite the existing file - so only a single entry will appear to be written.
You will want to append or write all instead.
System.IO.File.Delete(#"H:\Compare.txt");
foreach (var single103 in lstStore103)
{
foreach (var single101 in lstStore101)
{
if (single101 == single103)
{
checkResults.Add(single103);
System.IO.File.AppendAllText(#"H:\Compare.txt", single103 + Environment.NewLine);
}
}
}
or (if neither lstStore103 nor lstStore101 have duplicates):
System.IO.File.Delete(#"H:\Compare.txt");
foreach (var value in lstStore103.Intersect(lstStore101))
{
checkResults.Add(value);
System.IO.File.AppendAllText(#"H:\Compare.txt", value + Environment.NewLine);
}
The break; is responsible, it will leave the loop.
But you also don't want to use WriteAllText which rewrites the whole text-file but you want to append a new line. I would use this approach:
string startPattern = " :22:";
List<string> lstStoreTwentyOne = File.ReadLines(path)
.Where(l => l.StartsWith(startPattern))
.Select(l => l.Substring(startPattern.Length))
.ToList();
This will create and fill the list. I don't know how this is related to the lstStore103-list.
However, this will write all to the text-file and replaces your loops:
var matchingItems = lstStore103.Intersect(lstStore101);
File.WriteAllLines(#"H:\Compare.txt", matchingItems);
Related
I've searched around for a solution to this question but can't find an applicable circumstance and can't get my head around it either.
I've got a List<String[]> object (a parsed CSV file) and want to remove any rows if the first value in the row is equal to my criteria.
I've tried the following (with variations) and can't seem to get it to delete the lines, it just passes over them:
rows.RemoveAll(s => s[0].ToString() != "Test");
Which I'm currently reading as, remove s if s[0] (the first value in the row) does not equal "Test".
Can someone point me in the right direction for this?
Thanks, Al.
Edit for wider context / better understanding:
The code is as follows:
private void CleanUpCSV(string path)
{
List<string[]> rows = File.ReadAllLines(path).Select(x => x.Split(',')).ToList();
rows.RemoveAll(s => s[0] != "Test");
using (StreamWriter writer = new StreamWriter(path, false))
{
foreach (var row in rows)
{
writer.WriteLine(row);
}
}
}
So the question is -> Why won't this remove the lines that do not start with "Test" and upon writing, why is it returning System.String[] as all the values?
Did you try with Where? Where is going to filter based on a predicate. You should be able to do something like this:
Demo: Try it online!
List<string[]> rows = new List<string[]> { new []{"Test"}, new []{ "Foo"} };
rows = rows.Where(s => s[0] == "Test").ToList();
foreach(var row in rows)
{
Console.WriteLine(string.Join(",", row));
}
output
Test
You dont need ToString() because S[0] is already a string
You may want to handle empty case or s[0] could throw
You can use s.First() instead of s[0]
You can learn more about Predicateon msdn
Edit
For your example:
private void CleanUpCSV(string path)
{
var rows = File.ReadAllLines(path).Select(x => x.Split(','));
using (StreamWriter writer = new StreamWriter(path, false))
{
foreach (var row in rows.Where(s => s[0] == "Test"))
{
writer.WriteLine(string.Join(",", row));
}
}
}
By the way, you may want to use a library to handle csv parsing. I personally use CsvHelper
The only error in your code is the following:
Since row is string[] this
writer.WriteLine(row);
won't give you the result you were expecting.
Change it like this
writer.WriteLine(String.Join(",", row));
To convert the string[]back into its orginal form.
Any other "optimisation" in all the answers proposed here arent really optimal either.
If you're really trying to remove items where the first element isn't "Test", then your code should work, though you don't need to call .ToString() on s[0] since it's already a string. If this doesn't work for you, perhaps your problem lurks elsewhere? If you give an example of your code in a wider context you could get more help
Filter it like this instead:
var filteredList = rows.Where(s => s[0] == "test").ToArray();
with the following code Im trying to read a csv file that contains double values and convert it into a list. If I want to print that list The output just contains "system.collections.generic.list1 system.string". What is wrong in my code?
var filePath = #"C:\Users\amuenal\Desktop\Uni\test.csv";
var contents = File.ReadAllText(filePath).Split(';');
var csv = from line in contents
select line.Split(';').ToList();
foreach (var i in csv)
{
Console.WriteLine(i);
}
You got a couple things wrong with your code. First, you should most likely be using ReadAllLines() instead of ReadAllText(). Secondly, your LINQ query was returning a List<List<string>> which I imagine is not what you wanted. I would try something like this:
var filePath = #"C:\Users\amuenal\Desktop\Uni\test.csv";
//iterate through all the rows
foreach (var row in File.ReadAllLines(filePath))
{
//iterate through each column in each row
foreach(var col in row.Split(';'))
{
Console.WriteLine(col);
}
}
This should do good. Hope this helps.
var filePath = #"C:\Users\amuenal\Desktop\Uni\test.csv";
var contents = File.ReadAllLines(filePath);
var csv = (from line in contents
select line.Split(';')).SelectMany(x1 => x1);
foreach (var i in csv)
{
Console.WriteLine(i);
}
csv is an IEnumerable of a List of string. (in other words, each "i" is a list of string).
You need two loops:
foreach (var list in csv)
{
foreach(var str in list)
{
Console.WriteLine(str);
}
}
I've an array. Let's say;
private string[] WhiteList =
{
"abcxyz.cs",
"mouseapi.dll",
"SensitivtyFix.asi",
"drake.mp3"
};
Now I want to exclude that array from a directory/file search. How do I achieve that?
For this search, I'm using another array called BlackList from which I could fetch desired files but I'm unable to exclude files from the array Whitelist.
for (int i = 0; i < GetSize; ++i)
{
foreach (var File in DirInf.GetFiles("*", SearchOption.AllDirectories))
{
if (File.FullName.Contains(Blacklist[i]) && !File.FullName.Contains(WhiteList[i]))
{
listBox1.Items.Add("File: " + File.FullName);
}
}
}
The condition you need (for use with LINQ Where function) is
Predicate<FileInfo> filter =
f => Array.Exists(blackList, f.FullName.Contains) &&
!Array.Exists(whiteList, f.FullName.Contains);
The problem with your original code is that it looked at blackList[i] and whiteList AT THE SAME i, but the list index that matches might be different.
Now you may fill your list box:
foreach (var file in DirectoryInfo.GetFiles("*", SearchOption.AllDirectories).Where(filter))
listBox1.Items.Add(file);
YOu need
if (BlackList.Contains(File.FullName) && !WHiteList.Contains(File.FullName)))
although that seems odd, since you can just not put the whitelist names in the blacklist. Maybe you need || not &&
At the moment I have my code to get some files from a Dir.
foreach (var file in
Directory.GetFiles(MainForm.DIRECTORY_PATH, "*.csv"))
{
//Process File
string[] values = File.ReadAllLines(file)
.SelectMany(lineRead => lineRead.Split(',')
.Select(s => s.Trim()))
.ToArray();
I want to be able to order these file by date order first before i start reading them and processing them.
I looked at a suggestion on MDSN to use DirectoryInfo:
DirectoryInfo DirInfo = new DirectoryInfo(MainForm.DIRECTORY_PATH);
var filesInOrder = from f in DirInfo.EnumerateFiles()
orderby f.CreationTime
select f;
foreach (var item in filesInOrder)
{
//Process File
string[] values = File.ReadAllLines(item )
.SelectMany(lineRead => lineRead.Split(',')
.Select(s => s.Trim()))
.ToArray();
}
this doesnt work however as the System.IO.File.ReadAllLine(file) seems to red line with the error as item is a string and not an actual file. :(
Does anyone know a solution to this or has had a similar issue? :)
Regards
J.
From MSDN File.ReadAllLines(string path) takes file path as input.
Opens a text file, reads all lines of the file, and then closes the file.
You have to pass file path:
string[] values = File.ReadAllLines(item.FullName)
your code:
foreach (var item in filesInOrder)
{
string[] values = File.ReadAllLines(item.FullName)
...............................
...............................
}
You can replace all of your chunk with following code via lambda expressions:
var values = DirInfo.EnumerateFiles().OrderBy(f => f.CreationTime)
.Select(x => File.ReadAllLines(x.FullName)
.SelectMany(lineRead => lineRead.Split(',')
.Select(s => s.Trim())).ToArray()
);
Your first code snippet reads all lines in one file, where as the second one reads from all files in the directory. So it is not very clear what you want to do.
The second code snippet cannot work, because the variable values is declared inside the loop. Its visibility scope is limited to the code block of the loop. The result will therefore never be visible outside of the loop.
var filesInOrder = from f in DirInfo.EnumerateFiles() ...;
var items = new List<string>();
foreach (FileInfo f in filesInOrder) {
using (StreamReader sr = f.OpenText()) {
while (!sr.EndOfStream) {
items.AddRange(sr.ReadLine().Split(','));
}
}
}
Here I define a List<string> before the loop that will hold all the items of all files. We need two loops: one that loops over the files (foreach) and one that reads the lines in each file and successively adds items to the list (while).
I have two string inputs with that have been split so each item is distinguishable from the other (these items are product properties such as length, weight, finish, stain color, etc.). One string has the original values for the product while the other has the new values (whatever they may be--users can edit whichever product values they want).
Right now, I have the split strings producing the info that I want but I am not sure how to compare the results to each other since the individual product values are inside foreach loops.
This is what I have:
//checks to see which attributes have been changed/edited for the product
protected bool ifAttrChanged(string oldItm, string newItm)
{
bool retVal = false;
//get the original attributes of the product
string[] oldItms = oldItm.Split(',');
foreach (string oItm in oldItms)
{
if (oItm != "")
{
string[] oldThings = oItm.Split(',');
string oldies = oldThings.GetValue(0).ToString();
}
}
//get whatever new values have been entered for the product
string[] newItms = newItm.Split(',');
foreach (string nItm in newItms)
{
if (nItm != "")
{
string[] newThings = nItm.Split(',');
string newbies = newThings.GetValue(0).ToString();
}
}
if (newItms.ToString().Equals(oldItms.ToString(), StringComparison.Ordinal))
{
retVal = false;
Label3.Text += retVal.ToString() + "<br />";
}
else
{
retVal = true;
Label3.Text += retVal.ToString() + "<br />";
}
return retVal;
}
I would really like to compare the oldies string variable with the newbies string variable (weird names but I am not concerned about that) but they are each in their own foreach statement and I cannot access them elsewhere. How can I compare the old and new values of the two split strings successfully so I can obtain which values were changed and which values remained the same?
With all the strings splittend and stored in arrays (oldItems and newItems), and using System.Linq.
Try this:
var changedResults = newItems.Where(x => !oldItems.Any(y => x == y));
With this you will get a IEnumerable with all the string in newItems which no appear in oldItems array.
If you want to convert this to a list or something, add this:
var changedResults = newItems.Where(x => !oldItems.Any(y => x == y)).ToList();
I hope this helps
What is a "change"? Addition? Deletion? Modification?
If only addition, Oscar's method works. This is the same thing (set difference) but a little more concise:
var changedResults = newItems.Except(oldItems)
However, if a deletion is a "change" as well, you would need to consider it from both sides.
var changedResults = newItems.Except(oldItems).Union(oldItems.Except(newItems));
Since the sets are of only strings, there is no notion of modification.