Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

C# Search file for a string and get the string's line number

Tags:

c#

io

lines

I've been at this all day and I'm struggling to do it, I've searched far and wide and though there are similar questions, there are no answers.

What I'm trying to do: I'm trying to search a file for multiple strings and receive the line numbers so that when users wish to change said strings inside my application, my application knows what lines to replace with their string.

What I've tried so far: I've tried the below code, my attempt was to read the file then receive the line number, however it doesn't seem to be working at all.

            var lines = File.ReadAllLines(filepath);
            foreach (var match in lines // File.ReadLines(filePath)
                            .Select((text, index) => new { text, lineNumber = index + 1 })
                            .Where(x => x.text.Contains("motd=")))
            {
                lines[match.lineNumber] = "motd=" + textBox1.Text;
                File.WriteAllLines(filepath, lines);
            }
        }

What I expect the above code to do is find the string "motd=" in the file, get the line number and attempt to re-write it with what the user has inputted.

However, I receive this error: "Index was outside the bounds of the array".

like image 421
Tayla Wilson Avatar asked Jan 08 '23 15:01

Tayla Wilson


2 Answers

I think a for loop makes more sense here

var lines = File.ReadAllLines(filepath);
for (int i = 0; i < lines.Length; i++)
{
    if(lines[i].Contains("motd="))
    {
        lines[i] = "motd=" + textBox1.Text;
    }
}

File.WriteAllLines(filepath, lines);

A couple of issues with your code was that you were writing out the file in side the loop and you incremented the index by 1 which would then point to the wrong line in the array and result in the exception you are getting if the last line contains the text you are searching for.

It should be noted that this could be a memory hog if you are working with a really large file. In that case I'd read the lines in one at a time and write them out to a temp file, then delete the original file and rename the temp at the end.

var tempFile = Path.GetTempFileName();
using (var file = File.OpenWrite(tempFile))
using (var writer = new StreamWriter(file))
{
    foreach (var line in File.ReadLines(filepath))
    {
        if (line.Contains("motd="))
        {
            writer.WriteLine("motd=" + textBox1.Text);
        }
        else
        {
            writer.WriteLine(line);
        }
    }
}

File.Delete(filepath);
File.Move(tempFile, filepath);
like image 168
juharr Avatar answered Jan 22 '23 10:01

juharr


This will be much faster, and use less memory, and work with very large files, but you need to write to a new file:

File.WriteAllLines(filepath2,
  File
    .ReadLines(filepath)
    .Select(x=>x.Contains("motd=")?"motd="+TextBox1.Text:x));

It uses ReadLines which reads each line as it can instead of reading the entire file and breaking it into lines in one step. So it processes it as quickly as your file system can read, processes the line, then writes the result.

A complete replacement would look like this:

var filepath2= Path.GetTempFileName();
File.WriteAllLines(filepath2,
  File
    .ReadLines(filepath)
    .Select(x=>x.Contains("motd=")?"motd="+TextBox1.Text:x));
File.Delete(filepath);
File.Move(filepath2, filepath);

To do multiple values:

var filepath2= Path.GetTempFileName();
File.WriteAllLines(filepath2,
  File
    .ReadLines(filepath)
    .Select(x=> {
      if (x.Contains("motd=")) return "motd="+TextBox1.Text;
      if (x.Contains("author=")) return "author="+TextBox2.Text;
      return x;
    }));
File.Delete(filepath);
File.Move(filepath2, filepath);

or move the replace logic to it's own function:

string ReplaceTokens(string src)
{
  if (src.Contains("motd=")) return "motd="+TextBox1.Text;
  if (src.Contains("author=")) return "author="+TextBox2.Text;
  return src;
}
var filepath2= Path.GetTempFileName();
File.WriteAllLines(filepath2,
  File
    .ReadLines(filepath)
    .Select(ReplaceTokens));
File.Delete(filepath);
File.Move(filepath2, filepath);
like image 44
Robert McKee Avatar answered Jan 22 '23 10:01

Robert McKee