C#: Prepending to beginning of a file

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?

Answers


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).


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 );

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.


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());  

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);
                }
            }
        }
    }
}

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.)


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.


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);
                }
            }
        }
    }
}

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();
        }

// 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.


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.


Need Your Help

Visual Studio - vdproj is incompatible

visual-studio visual-studio-2010 visual-studio-2012 visual-studio-2017

I have a project in a solution I am working on in Visual Studio 2012, and I get this message for one of the projects: "This version of Visual Studio does not have the following project types instal...

How to include emoticons in Swift string?

string swift nsstring

Here is a pretty good article that references iOS emoticons and their code. For example \ue008 for the small camera.