1

How can I remove a whole line from a text file if the first word matches to a variable I have?

What I'm currently trying is:

List<string> lineList = File.ReadAllLines(dir + "textFile.txt").ToList();
lineList = lineList.Where(x => x.IndexOf(user) <= 0).ToList();
File.WriteAllLines(dir + "textFile.txt", lineList.ToArray());

But I can't get it to remove.

Omar
  • 16,329
  • 10
  • 48
  • 66
user1213488
  • 473
  • 2
  • 7
  • 19

4 Answers4

2

The only mistake that you have is you are checking <= 0 with indexOf, instead of = 0.

-1 is returned when the string does not contain the searched for string.

<= 0 means either starts with or does not contain

=0 means starts with <- This is what you want

Stefan H
  • 6,635
  • 4
  • 24
  • 35
  • 1
    +1, just keep in mind that `IndexOf(...) == 0` is not idiomatic for "starts with" and may confuse future developers. – user7116 May 18 '12 at 18:07
  • @sixlettervariables: only the bad ones :p – banging May 18 '12 at 18:07
  • 2
    @user1213488 just as a warning, you may encounter issues with the concept of first "word". if user = abc, and the first "word" is actually abcdef, that line will still be returned. You might need to do some more thorough parsing of the first "word" – Stefan H May 18 '12 at 18:09
  • @banging as I recall you were confused with = 0 vs < 0 in your own answer :P – Stefan H May 18 '12 at 18:10
  • @StefanH: I never claimed I was one of the good ones ;D But seriously I was confused with the requirements and not what =0 or <0 mean! – banging May 18 '12 at 18:31
1

This method will read the file line-by-line instead of all at once. Also note that this implementation is case-sensitive.

It also assumes you aren't subjected to leading spaces.

using (var writer = new StreamWriter("temp.file"))
{
    //here I only write back what doesn't match
    foreach(var line in File.ReadLines("file").Where(x => !x.StartsWith(user)))
        writer.WriteLine(line);  // not sure if this will cause a double-space ?
}

File.Move("temp.file", "file");
Austin Salonen
  • 49,173
  • 15
  • 109
  • 139
1

You were pretty close, String.StartsWith handles that nicely:

// nb: if you are case SENSITIVE remove the second argument to ll.StartsWith
File.WriteAllLines(
    path,
    File.ReadAllLines(path)
        .Where(ll => ll.StartsWith(user, StringComparison.OrdinalIgnoreCase)));

For really large files that may not be well performing, instead:

// Write our new data to a temp file and read the old file On The Fly
var temp = Path.GetTempFileName();
try
{
    File.WriteAllLines(
        temp,
        File.ReadLines(path)
            .Where(
               ll => ll.StartsWith(user, StringComparison.OrdinalIgnoreCase)));
    File.Copy(temp, path, true);
}
finally
{
    File.Delete(temp);
}

Another issue noted was that both IndexOf and StartsWith will treat ABC and ABCDEF as matches if the user is ABC:

var matcher = new Regex(
    @"^" + Regex.Escape(user) + @"\b", // <-- matches the first "word"
    RegexOptions.CaseInsensitive);
File.WriteAllLines(
    path,
    File.ReadAllLines(path)
        .Where(ll => matcher.IsMatch(ll)));
user7116
  • 63,008
  • 17
  • 141
  • 172
0
Use  `= 0` instead of `<= 0`. 
Somnath Muluk
  • 55,015
  • 38
  • 216
  • 226
banging
  • 2,540
  • 1
  • 22
  • 26
  • 1
    You might want to elaborate that IndexOf will return -1 when there is no match, therefore the OP is currently returning all lines that both start with user, and do not have user. – Stefan H May 18 '12 at 17:54
  • And I think you mean = 0 (Starts with) NOT <=0 (either starts with or does not contain) and NOT <0 (does not contain) – Stefan H May 18 '12 at 17:57