0

I already have a solution for this problem, but it seems very confusing and unpractical to me.

What I intend to do is to read a file line-by-line (due to the file size, it's unpractical to load it into memory all at once), and if certain condition is met (e.g.: the line matches a regex pattern, or contains certain keywords, or is equal to certain string)

Here's what I'd Ideally have:

 void TryGetLineIf(string filePath, Condition condition, out string desiredLine)
  {
     StreamReader fileReader = new StreamReader(filePath);
     string currentLine = "";

     while (!fileReader.EndOfStream)
     {
        currentLine = fileReader.ReadLine();

        if (condition)
        {
           desiredLine = currentLine;
        }
     }
  }

However, I don't know what to do with the condition parameter. The only way out I can think of is to replace the condition with an enum, (LineSelectionOptions.IsRegexMatch, LineSelectionOptions.ContainsString ...), add an extra parameter to the void and switch between possible values for it. I'm working with .NET 2.0, if that is relevant.

Gonzalo.-
  • 12,512
  • 5
  • 50
  • 82
Caio
  • 35
  • 5
  • What do you mean you don't know what to do with the condition? You don't know how to write the logic for the condition? – CodingYoshi Mar 02 '17 at 02:15
  • Yes. I don't know how to make the condition check if the line (in the file) currently being read satisfies the criteria for it to be "chosen" – Caio Mar 02 '17 at 02:24
  • Possible duplicate of [Passing a condition as a parameter](http://stackoverflow.com/questions/7166307/passing-a-condition-as-a-parameter) – Thewads Mar 02 '17 at 10:12
  • @Gonzalo.- Not sure if you can see my reply below, so I'm tagging you here as well – Caio Mar 02 '17 at 20:50

3 Answers3

4

If you know the parameters your function will have, you could use Func to pass a function which will return a bool

your method definition will be like this

 void TryGetLineIf(string filePath, Func<string, bool> condition, out string desiredLine)

The if-line will be like this

if(condition(currentLine)) {
   desiredLine = currentLine;
}

and the call to the method will be something like this

Func<string, bool> condition = (line) => line.Length > 1;
string desiredLine;
TryGetLineIf("C:\\....file.pdf", condition, out desiredLine)

BUT since you're working in 2.0, you might want to simulate Func using delegates See this How to simulate a "Func<(Of <(TResult>)>) Delegate" in .NET Framework 2.0? or this Replacing Func with delegates C#

Community
  • 1
  • 1
Gonzalo.-
  • 12,512
  • 5
  • 50
  • 82
  • It's worth just adding in to your post the code to do this - `public delegate T Func(T2 arg);` - but maybe also explaining how to do this with a strongly typed delegate... – Dan Field Mar 02 '17 at 02:30
  • You really need `Func condition = (line) => line.Length > 1;` as `var condition = (line) => line.Length > 1;` doesn't compile. – Enigmativity Mar 02 '17 at 02:42
  • Sorry, I don't have a VS here so I was doing it by memory (or trying to be saved with var lol) – Gonzalo.- Mar 02 '17 at 02:42
  • 1
    Anyway don't need to simulate `Func` using delegates, because it's a delegate :D OP could declare the delegate as part of his/her own solution: `public delegate TResult Func(T input);`... And later make us of it as follows: `new Func(delegate(line) { return line.Length > 1; });`. – Matías Fidemraizer Mar 02 '17 at 03:42
0

Please excuse the formatting because I am on a mobile phone.

Anyhow, I think this is a perfect candidate for using yield return because the caller can decide to break and not want to read the rest of the file after any line. This will allow that. Plus the caller can perform chaining and do further processing. Furthermore, no need for an out parameter.

Make sure to use the using statement and if available in .NET 2.0, use the File.ReadLines method instead: it reads line by line and it's much simpler. I will try and edit my answer with the suggestions I have made once I get home.

public static IEnumerable<string> TryGetLineIf(string filePath, Condition condition
{
     StreamReader fileReader = new StreamReader(filePath);
     string currentLine = "";

 while (!fileReader.EndOfStream)
 {
    currentLine = fileReader.ReadLine();

    if (condition.MeetsCriteria(currentLine))
    {
         yield  return currentLine;
    }

    }
} 

And as a final note it is more robust if you pass a delegate as the condition instead of a class instance as suggested in another answer.

CodingYoshi
  • 25,467
  • 4
  • 62
  • 64
0

@Gonzalo.- Thanks for the answer, I managed to use it in a few cases. But now I have a problem when attempting to do that with more parameters in the Func delegate, e.g, when trying to check if the line matches a certain Regex pattern:

Func<string, string, bool> condition = 
(line, pattern) => new Regex(pattern).IsMatch(line)

IEnumerable<string> LinesIWant =
ReturnLineIf("C:\test.txt", condition);

And here's the method:

IEnumerable<string> ReturnLineIf(string filePath, Func<string, string, bool> condition)
{
  // (snip)
  // How do I specify a pattern when calling the method?
  if (condition(currentLine, "This should be the pattern..."))
  {
    yield return currentLine;
  }
}
Caio
  • 35
  • 5
  • I don't understand your question now. And maybe you should update the original question, adding new info at the bottom, rather than posting this as an answer – Gonzalo.- Mar 02 '17 at 20:54