JavaNoob
JavaNoob

Reputation: 3632

How to delete last line in a text file?

I have a simple log text file with the extension of .txt with a white space line at the end of that text file every time the log file is generated from a 3rd party program.

Therefore is there any methods or codes that I can utilize to delete the last line of the text file?

An example of the log text file:

Sun Jul 22 2001 02:37:46,73882,...b,r/rrwxrwxrwx,0,0,516-128-3,C:/WINDOWS/Help/digiras.chm
Sun Jul 22 2001 02:44:18,10483,...b,r/rrwxrwxrwx,0,0,480-128-3,C:/WINDOWS/Help/cyycoins.chm
Sun Jul 22 2001 02:45:32,10743,...b,r/rrwxrwxrwx,0,0,482-128-3,C:/WINDOWS/Help/cyzcoins.chm
Sun Jul 22 2001 04:26:14,174020,...b,r/rrwxrwxrwx,0,0,798-128-3,C:/WINDOWS/system32/spool/drivers/color/kodak_dc.icm

Upvotes: 14

Views: 40462

Answers (7)

Theodor Zoulias
Theodor Zoulias

Reputation: 43545

It is actually possible to delete the last line of a text file directly, without creating a new file and copying all the lines of the existing file except from the last line. Deleting the last line can be done simply with the method FileStream.SetLength:

// Sets the length of this stream to the given value.
public override void SetLength(long value);

So all you have to do is to find where the last line of the file starts, and set the length of the file in that position.

stream.SetLength(newLength);

The advantages of deleting the last line using the SetLength are numerous:

  1. You don't have to apply to the new file the security permission of the old file.
  2. You don't have to worry about changing the encoding of the file.
  3. It is much faster if the file is huge.
  4. If done frequently, it doesn't cause excessive wear to the hardware device (SSD).

The disadvantage is that finding the location where the last line starts is not trivial. It is doable though, provided that either:

  • The size of the file is within the range that reading it from the start is acceptable.
  • Or that you can make some reasonable assumptions about the size of the lines in the file.

The implementation below makes two assumptions: That the lines in the file are separated with the CR+LF combination, and that no line in the file can be longer than 10,000 bytes.

/// <summary>
/// Deletes the last line of a text file,
/// that has the character combination CR+LF as line seperator,
/// unless the last line is longer than maxExpectedLineLength in bytes,
/// or the file has only one line.
/// </summary>
public static bool DeleteLastLine(string filePath, int maxExpectedLineLength = 10000)
{
    using FileStream stream = File.Open(filePath, FileMode.Open,
        FileAccess.ReadWrite, FileShare.None);
    long fileSize = stream.Length;
    if (fileSize == 0) return false;
    byte[] lastBytes = new byte[Math.Min(maxExpectedLineLength, fileSize)];
    stream.Position = fileSize - lastBytes.Length;
    stream.Read(lastBytes, 0, lastBytes.Length);

    // Find the last character that is not a line separator
    int lastNonCrLf = -1;
    for (int i = lastBytes.Length - 1; i >= 0; i--)
    {
        if (lastBytes[i] != '\r' && lastBytes[i] != '\n')
        {
            lastNonCrLf = i; break;
        }
    }

    // Find the last line separator that precedes any non-CR-LF character
    int lastCrLf = -1;
    for (int i = lastNonCrLf - 2; i >= 0; i--)
    {
        if (lastBytes[i] == '\r' && lastBytes[i + 1] == '\n')
        {
            lastCrLf = i; break;
        }
    }

    if (lastCrLf == -1) return false;
    long newLength;
    if (lastNonCrLf == lastBytes.Length - 1)
    {
        // There was no trailing \r\n originally, so delete the last \r\n
        newLength = fileSize - lastBytes.Length + lastCrLf;
    }
    else
    {
        // Preserve the last \r\n, to preserve the existing style
        newLength = fileSize - lastBytes.Length + lastCrLf + 2;
    }
    stream.SetLength(newLength);
    return true;
}

The reason that the method DeleteLastLine doesn't delete the very last line (if the file has only one), is because in that case it would truncate the file to zero bytes, removing inadvertently the BOM of the file if present. In case removing the very last line is important, you may have to make more assumptions about the file having no BOM, or that you can be sure about what the BOM is (Encoding.Preamble maybe?) so that you set the length of the file to the length of the BOM.

Upvotes: 0

maoleigepu
maoleigepu

Reputation: 153

I just find a solution from other website, and this is the url https://stackoverrun.com/cn/q/627722 .

With a small number of lines, you could easily use something like this

string filename = @"C:\Temp\junk.txt";
string[] lines = File.ReadAllLines(filename);

However, as the files get larger, you may want to stream the data in and out with something like this

string filename = @"C:\Temp\junk.txt";
string tempfile = @"C:\Temp\junk_temp.txt";

using (StreamReader reader = new StreamReader(filename))
{                
       using (StreamWriter writer = new StreamWriter(tempfile))
       {
           string line = reader.ReadLine();

           while (!reader.EndOfStream)
           {
               writer.WriteLine(line);
               line = reader.ReadLine();
           } // by reading ahead, will not write last line to file
       }
}

File.Delete(filename);
File.Move(tempfile, filename);

Upvotes: 1

mortal
mortal

Reputation: 235

That should be the best option for using with big files.

using (StreamReader source = new StreamReader(sourceFileName))
        {
            using (StreamWriter sw = new StreamWriter(newFileName))
            {
                do
                {

                    line = source.ReadLine();

                    if (source.Peek() > -1)
                    {
                        sw.WriteLine(line);
                    }
                    else
                    {
                        File.AppendAllText("RemovedLastLine.txt", line);
                    }

                }
                while (line != null);
            }
        }

Upvotes: 0

user5849346
user5849346

Reputation:

You can't delete the line end, as File.WriteAllLines automatically adds it, however, you can use this method:

public static void WriteAllLinesBetter(string path, params string[] lines)
{
    if (path == null)
        throw new ArgumentNullException("path");
    if (lines == null)
        throw new ArgumentNullException("lines");

    using (var stream = File.OpenWrite(path))
    using (StreamWriter writer = new StreamWriter(stream))
    {
        if (lines.Length > 0)
        {
            for (int i = 0; i < lines.Length - 1; i++)
            {
                writer.WriteLine(lines[i]);
            }
            writer.Write(lines[lines.Length - 1]);
        }
    }
}

This is not mine, I found it at .NET File.WriteAllLines leaves empty line at the end of file

Upvotes: 2

ARrigo
ARrigo

Reputation: 93

If you want to delete last N lines from a file without loading all into memory:

int numLastLinesToIgnore = 10;
string line = null;
Queue<string> deferredLines = new Queue<string>();
using (TextReader inputReader = new StreamReader(inputStream))
using (TextWriter outputReader = new StreamWriter(outputStream))
{
    while ((line = inputReader.ReadLine()) != null)
    {
        if (deferredLines.Count() == numLastLinesToIgnore)
        {
            outputReader.WriteLine(deferredLines.Dequeue());
        }

        deferredLines.Enqueue(line);
    }
    // At this point, lines still in Queue get lost and won't be written
}

What happens is that you buffer each new line in a Queue with dimension numLastLinesToIgnore and pop from it a line to write only when Queue is full. You actually read-ahead the file and you are able to stop numLastLinesToIgnore lines before the end of file is reached, without knowing the total number of lines in advance.

Note that if text has less than numLastLinesToIgnore, result is empty.

I came up with it as a mirror solution to this: Delete specific line from a text file?

Upvotes: 6

Migwell
Migwell

Reputation: 20127

Use this method to remove the last line of the file:

public static void DeleteLastLine(string filepath)
{
    List<string> lines = File.ReadAllLines(filepath).ToList();

    File.WriteAllLines(filepath, lines.GetRange(0, lines.Count - 1).ToArray());

}

Edit: Realized the lines variable didn't exist previously, so I updated the code.

Upvotes: 11

decyclone
decyclone

Reputation: 30830

How about something like :

var lines = System.IO.File.ReadAllLines("...");
System.IO.File.WriteAllLines("...", lines.Take(lines.Length - 1).ToArray());

Explanation:

Technically, you don't delete a line from a file. You read the contents of a file and write them back excluding the content you want deleted.

What this code does is read all the lines into an array, and write these lines back to the file excluding only the last line. (Take() method (Part of LINQ) takes number of lines specified which in our case, is length - 1). Here, var lines can be read as String[] lines.

Upvotes: 28

Related Questions