I am trying to use a loop to collect all the elements in a text file and select out of those elements specific ones to display.
{
string lines = File.ReadLines(path).Where(line => line.StartsWith("Name: ")).ToString();
foreach (string line in lines)
{
MessageList.Items.Add(lines);
}
}
The idea with this code is for the file stream to parse the entire document and only select the lines that start with Name:, ignoring all other ones so I can add the
I can't seem to get around the syntax error within the condition of the foreach loop. It says I'm trying to convert between char and string and the compiler is confused by my request. I've tried doing this with and without invoking ToString(), I've also tried it by declaring lines as a var instead of a string. I tried to do this without the lambda expression
lines is not a string. It will be of type
IEnumerable<string>
if you let it.
If you use var instead the compiler will figure out the type for you. If you hover your mouse over the lines or line variable in your IDE it will let you know the type.
var lines = File.ReadLines(path).Where(line => line.StartsWith("Name: "));
foreach (var line in lines)
{
MessageList.Items.Add(line);
}
If you want to be explicit about the type, this would be the code.
IEnumerable<string> lines = File.ReadLines(path).Where(line =>
line.StartsWith("Name: "));
foreach (string line in lines)
{
MessageList.Items.Add(line);
}
About var - https://learn.microsoft.com/en-us/dotnet/csharp/language-reference/keywords/var
About File.Readlines including the return type - https://learn.microsoft.com/en-us/dotnet/api/system.io.file.readlines?view=net-6.0
You could also use:-
File.ReadLines(path).Where(line => line.StartsWith("Name: ")).ToList()
.ForEach(line => { MessageList.Items.Add(line); });
Related
I'm using this code to read csv files and make List out of them:
public static IEnumerable<string> appendLineNumbers(IEnumerable<string> lines)
{
return lines
.Select((line, index) => new {
Line = line,
LineNumber = index
.ToString()
.Equals("0", StringComparison.CurrentCultureIgnoreCase)
? "RowNumber"
: (index + 1).ToString()})
.Select(x => $"{x.LineNumber};{x.Line}");
}
As it is right now, when I read a file it will add a RowNumber even to a completed empty row. The result will be :
RowNumer;
But I expect the line to have as many ";" as the Headline. Is it possible to put this into this statement or do I need to make an extra method for this.
I would say right at the beginning remove the empty lines. The rest of your code should work then without this stumblestones of enumerating empty lines:
return lines.Where(x=>!string.IsNullOrWhiteSpace(x))
. // here the rest of your code
If I missunderstood you please drop me a comment
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();
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 am reading in lines from a large text file. Amongst these file are occasional strings, which are in a preset list of possibilities, and I wish to check the line currently being read for a match to any of the strings in the possibilities list. If there is a match I want to simply append them to a different list, and continue the loop I am using to read the file.
I was just wondering if there is a more efficent way to do a line.Contains() or equivilance check against say the first element in the list, then the second, etc. without using a nested loop or a long if statement filled with "or"s.
Example of what I have now:
List<string> possible = new List<string> {"Cat", "Dog"}
using(StreamReader sr = new StreamReader(someFile))
{
string aLine;
while ((aLine = sr.Readline()) != null)
{
if (...)
{
foreach (string element in possible)
{
if line.Contains(element) == true
{
~add to some other list
continue
}
}
~other stuff
}
}
I don't know about more efficient run-time wise, but you can eliminate a lot of code by using LINQ:
otherList.AddRange(File.ReadAllLines(somefile).
.Where(line => possible.Any(p => line.Contains(p)));
I guess you are looking for:
if(possible.Any(r=> line.Contains(r)))
{
}
You can separate your work to Get Data and then Analyse Data. You don't have to do it in the same loop.
After reading lines, there are many ways to filter them. The most readable and maintenable IMO is to use Linq.
You can change your code to this:
// get lines
var lines = File.ReadLines("someFile");
// what I am looking for
var clues = new List<string> { "Cat", "Dog" };
// filter 1. Are there clues? This is if you only want to know
var haveCluesInLines = lines.Any(l => clues.Any(c => l.Contains(c)));
// filter 2. Get lines with clues
var linesWithClues = lines.Where(l => clues.Any(c => l.Contains(c)));
Edit:
Most likely you will have little clues and many lines. This example checks each line with every clue, saving time.
I am trying to read a file and process using LINQ.
I have a exclude list where if i encounter certain words in the file, i should omit that line
my code is
string sCodeFile = #"C:\temp\allcode.lst";
List<string> sIgnoreList = new List<string>() { "foo.c", "foo1.c" };
var wordsPerLine = from line in File.ReadAllLines(sCodeFile)
let items = line.Split('\n')
where !line.Contains(sIgnoreList.ToString())
select line;
foreach (var item in wordsPerLine)
{
console.WriteLine(item);
}
My LST file looks like below
\voodoo\foo.c
\voodoo\voodoo.h
\voodoo\std.c
\voodoo\foo1.h
in the end i want only
\voodoo\voodoo.h
\voodoo\std.c
How can i process the ignored list in contains? with my above code i dont get the desired output for sure
can any one help?
regards,
Karthik
Revised my answer. The bug is that you're doing a ToString on the ignore list, which certainly will not work. You must check each item in the list, which can be done using something like this:
where !sIgnoreList.Any(ignore => line.Contains(ignore))
A curiosity: since the above lambda is just passing a value into a method that only take the value as a parameter, you can write this even more compact as a method group like this:
where !sIgnoreList.Any(line.Contains)
Try this.
string sCodeFile = #"C:\temp\allcode.lst";
List<string> sIgnoreList = new List<string>() { "foo.c", "foo1.c" };
var wordsPerLine = File.ReadAllLines(sCodeFile).Where(n =>
{
foreach (var ign in sIgnoreList)
{
if (n.IndexOf(ign) != -1)
return false;
}
return true;
});
It passes the current element (n) to a lambda function, which checks it against every element of the sIgnoreList. Returning false means the element is ignored, true means it's returned.
Change it to:
where !sIgnoreList.Contains(line)
You need to compare each single line and check that it doesn't exist in the ignore list.
That's why the Vladislav's answer did not work.
Here's the working solution:
var result = from line in File.ReadAllLines(codeFile)
where !ignoreList.Any(line.Contains)
select line;
The problem was you didn't want to check for the whole path and messed up words/lines part a bit.