51

What is the best way to add text to the beginning of a file using C#?

I couldn't find a straightforward way to do this, but came up with a couple work-arounds.

  1. Open up new file, write the text that I wanted to add, append the text from the old file to the end of the new file.

  2. Since the text I want to add should be less than 200 characters, I was thinking that I could add white space characters to the beginning of the file, and then overwrite the white space with the text I wanted to add.

Has anyone else come across this problem, and if so, what did you do?

jmoreno
  • 12,752
  • 4
  • 60
  • 91
Grace
  • 1,103
  • 1
  • 8
  • 10
  • 1
    Interesting question. Clearly the framework (and no doubt the underlying file system) is append-biased, since File.AppendText and File.AppendAllText are provided without corresponding prepend methods. Makes sense -- bytes can be appended without "moving" the existing content. – harpo Aug 27 '09 at 18:45
  • 1
    hmm ok, so the answers seem to favor option 1 (either using a temp file or reading into memory). Is there a flaw with using option 2? – Grace Aug 27 '09 at 18:52
  • You can check out this http://stackoverflow.com/questions/1008742/adding-text-to-beginning-and-end-of-file-in-c post on Adding text to beginning and end of file in C#. Its for XML files, but easily modifiable for your use. – SwDevMan81 Aug 27 '09 at 18:50
  • How do i do this within an existing file, lets say it looks for a specific word that can be anywhere in the file? – someguy76 May 19 '17 at 15:47
  • I really like the second option; it's the best solution when creating huge files with a fixed size header. E.g. writing a recording (sampling) file and overwriting its header to specify the total duration and sample count on file closing. Thank you so much for auto-answering your question :). – fibriZo raZiel Apr 21 '22 at 16:47

11 Answers11

46

This works for me, but for small files. Probably it's not a very good solution otherwise.

string currentContent = String.Empty;
if (File.Exists(filePath))
{
    currentContent = File.ReadAllText(filePath);
}
File.WriteAllText(filePath, newContent + currentContent );
Luke Willis
  • 8,429
  • 4
  • 46
  • 79
Victor Martins
  • 1,355
  • 1
  • 12
  • 23
20

Adding to the beginning of a file (prepending as opposed to appending) is generally not a supported operation. Your #1 options is fine. If you can't write a temp file, you can pull the entire file into memory, preprend your data to the byte array and then overwrite it back out (this is only really feasible if your files are small and you don't have to have a bunch in memory at once because prepending the array is not necessarily easy without a copy either).

JP Alioto
  • 44,864
  • 6
  • 88
  • 112
3

using two streams, you can do it in place, but keep in mind that this will still loop over the whole file on every addition

using System;
using System.IO;
using System.Text;

namespace FilePrepender
{
    public class FilePrepender
    {
        private string file=null;
        public FilePrepender(string filePath)
        {
            file = filePath;
        }
        public void prependline(string line)
        {
            prepend(line + Environment.NewLine);
        }
        private void shiftSection(byte[] chunk,FileStream readStream,FileStream writeStream)
        {
            long initialOffsetRead = readStream.Position;
            long initialOffsetWrite= writeStream.Position;
            int offset = 0;
            int remaining = chunk.Length;
            do//ensure that the entire chunk length gets read and shifted
            {
                int read = readStream.Read(chunk, offset, remaining);
                offset += read;
                remaining -= read;
            } while (remaining > 0);
            writeStream.Write(chunk, 0, chunk.Length);
            writeStream.Seek(initialOffsetWrite, SeekOrigin.Begin);
            readStream.Seek(initialOffsetRead, SeekOrigin.Begin);
        }
        public void prepend(string text)
        {
            byte[] bytes = Encoding.Default.GetBytes(text);
            byte[] chunk = new byte[bytes.Length];
            using (FileStream readStream = File.Open(file, FileMode.Open, FileAccess.Read, FileShare.ReadWrite))
            {
                using(FileStream writeStream = File.Open(file, FileMode.OpenOrCreate, FileAccess.Write, FileShare.ReadWrite))
                {
                    readStream.Seek(0, SeekOrigin.End);//seek chunk.Length past the end of the file 
                    writeStream.Seek(chunk.Length, SeekOrigin.End);//which lets the loop run without special cases
                    long size = readStream.Position;
                    //while there's a whole chunks worth above the read head, shift the file contents down from the end
                    while(readStream.Position - chunk.Length >= 0)
                    {
                        readStream.Seek(-chunk.Length, SeekOrigin.Current);
                        writeStream.Seek(-chunk.Length, SeekOrigin.Current);
                        shiftSection(chunk, readStream, writeStream);
                    }
                    //clean up the remaining shift for the bytes that don't fit in size%chunk.Length
                    readStream.Seek(0, SeekOrigin.Begin);
                    writeStream.Seek(Math.Min(size, chunk.Length), SeekOrigin.Begin);
                    shiftSection(chunk, readStream, writeStream);
                    //finally, write the text you want to prepend
                    writeStream.Seek(0,SeekOrigin.Begin);
                    writeStream.Write(bytes, 0, bytes.Length);
                }
            }
        }
    }
}
Austin_Anderson
  • 900
  • 6
  • 16
3

Yeah, basically you can use something like this:

public static void PrependString(string value, FileStream file)
{
     var buffer = new byte[file.Length];

     while (file.Read(buffer, 0, buffer.Length) != 0)
     {
     }

     if(!file.CanWrite)
         throw new ArgumentException("The specified file cannot be written.", "file");

     file.Position = 0;
     var data = Encoding.Unicode.GetBytes(value);
     file.SetLength(buffer.Length + data.Length);
     file.Write(data, 0, data.Length);
     file.Write(buffer, 0, buffer.Length);
 }

 public static void Prepend(this FileStream file, string value)
 {
     PrependString(value, file);
 }

Then

using(var file = File.Open("yourtext.txt", FileMode.Open, FileAccess.ReadWrite))
{
    file.Prepend("Text you want to write.");
}

Not really effective though in case of huge files.

2

prepend:

private const string tempDirPath = @"c:\temp\log.log", tempDirNewPath = @"c:\temp\log.new";

        StringBuilder sb = new StringBuilder();
        ...
        File.WriteAllText(tempDirNewPath, sb.ToString());
        File.AppendAllText(tempDirNewPath, File.ReadAllText(tempDirPath));
        File.Delete(tempDirPath);
        File.Move(tempDirNewPath, tempDirPath);
        using (FileStream fs = File.OpenWrite(tempDirPath))
        {   //truncate to a reasonable length
            if (16384 < fs.Length) fs.SetLength(16384);
            fs.Close();
        }
3ric
  • 21
  • 2
2

I think the best way is to create a temp file. Add your text then read the contents of the original file adding it to the temp file. Then you can overwrite the original with the temp file.

Ryan Cook
  • 9,275
  • 5
  • 38
  • 37
2
// The file we'll prepend to
string filePath = path + "\\log.log";

// A temp file we'll write to
string tempFilePath = path + "\\temp.log";

// 1) Write your prepended contents to a temp file.
using (var writer = new StreamWriter(tempFilePath, false))
{
    // Write whatever you want to prepend
    writer.WriteLine("Hi");
}

// 2) Use stream lib methods to append the original contents to the Temp
// file.
using (var oldFile = new FileStream(filePath, FileMode.OpenOrCreate, FileAccess.Read, FileShare.Read))
{
    using (var tempFile = new FileStream(tempFilePath, FileMode.Append, FileAccess.Write, FileShare.Read))
    {
        oldFile.CopyTo(tempFile);
    }
}

// 3) Finally, dump the Temp file back to the original, keeping all its
// original permissions etc.
File.Replace(tempFilePath, filePath, null);

Even if what you're writing is small, the Temp file gets the entire original file appended to it before the .Replace(), so it does need to be on disk.

Note that this code is not Thread-safe; if more than one thread accesses this code you can lose writes in the file swapping going on here. That said, it's also pretty expensive, so you'd want to gate access to it anyway - pass writes via multiple Providers to a buffer, which periodically empties out via this prepend method on a single Consumer thread.

Chris Moschini
  • 36,764
  • 19
  • 160
  • 190
1

You should be able to do this without opening a new file. Use the following File method:

public static FileStream Open(
    string path,
    FileMode mode,
    FileAccess access
)

Making sure to specify FileAccess.ReadWrite.

Using the FileStream returned from File.Open, read all of the existing data into memory. Then reset the pointer to the beginning of the file, write your new data, then write the existing data.

(If the file is big and/or you're suspicious of using too much memory, you can do this without having to read the whole file into memory, but implementing that is left as an exercise to the reader.)

JSBձոգչ
  • 40,684
  • 18
  • 101
  • 169
1

The following algorithm may solve the problem pretty easily, it's most efficient for any size of file, including very big text files:

string outPutFile = @"C:\Output.txt";
string result = "Some new string" + DateTime.Now.ToString() + Environment.NewLine;
StringBuilder currentContent = new StringBuilder();
List<string> rawList = File.ReadAllLines(outPutFile).ToList();
foreach (var item in rawList) {
    currentContent.Append(item + Environment.NewLine);
}            
File.WriteAllText(outPutFile, result + currentContent.ToString());  
António Ribeiro
  • 4,129
  • 5
  • 32
  • 49
0

Put the file's contents in a string. Append new data you want to add to the top of the file to that string -- string = newdata + string. Then move the seek position of the file to 0 and write the string into the file.

David G
  • 94,763
  • 41
  • 167
  • 253
James
  • 1
0

Use this class:

public static class File2
{
    private static readonly Encoding _defaultEncoding = new UTF8Encoding(false, true); // encoding used in File.ReadAll*()
    private static object _bufferSizeLock = new Object();
    private static int _bufferSize = 1024 * 1024; // 1mb
    public static int BufferSize 
    {
        get
        {
            lock (_bufferSizeLock)
            {
                return _bufferSize;
            }
        }
        set
        {
            lock (_bufferSizeLock)
            {
                _bufferSize = value;
            }
        }
    }

    public static void PrependAllLines(string path, IEnumerable<string> contents)
    {
        PrependAllLines(path, contents, _defaultEncoding);
    }

    public static void PrependAllLines(string path, IEnumerable<string> contents, Encoding encoding)
    {
        var temp = Path.GetTempFileName();
        File.WriteAllLines(temp, contents, encoding);
        AppendToTemp(path, temp, encoding);
        File.Replace(temp, path, null);
    }

    public static void PrependAllText(string path, string contents)
    {
        PrependAllText(path, contents, _defaultEncoding);
    }

    public static void PrependAllText(string path, string contents, Encoding encoding)
    {
        var temp = Path.GetTempFileName();
        File.WriteAllText(temp, contents, encoding);
        AppendToTemp(path, temp, encoding);
        File.Replace(temp, path, null);
    }

    private static void AppendToTemp(string path, string temp, Encoding encoding)
    {
        var bufferSize = BufferSize;
        char[] buffer = new char[bufferSize];

        using (var writer = new StreamWriter(temp, true, encoding))
        {
            using (var reader = new StreamReader(path, encoding))
            {
                int bytesRead;
                while ((bytesRead = reader.ReadBlock(buffer,0,bufferSize)) != 0)
                {                   
                    writer.Write(buffer,0,bytesRead);
                }
            }
        }
    }
}
Ronnie Overby
  • 45,287
  • 73
  • 267
  • 346