C#:添加到文件开头

问题描述 投票:0回答:12

使用 C# 将文本添加到文件开头的最佳方法是什么?

我找不到直接的方法来做到这一点,但想出了一些解决方法。

  1. 打开新文件,写入我要添加的文本,将旧文件中的文本附加到新文件的末尾。

  2. 由于我要添加的文本应该少于200个字符,所以我想我可以在文件的开头添加空白字符,然后用我要添加的文本覆盖空白。

还有其他人遇到过这个问题吗?如果有,你是怎么做的?

c# file-io
12个回答
47
投票

这对我有用,但适用于小文件。否则这可能不是一个很好的解决方案。

string currentContent = String.Empty;
if (File.Exists(filePath))
{
    currentContent = File.ReadAllText(filePath);
}
File.WriteAllText(filePath, newContent + currentContent );

20
投票

添加到文件的开头(前置而不是附加)通常不受支持的操作。你的#1选项很好。如果您无法写入临时文件,您可以将整个文件拉入内存,将数据预先添加到字节数组中,然后将其覆盖(只有当您的文件很小并且您不必这样做时,这才真正可行)内存中一次有一堆,因为如果没有副本,在数组前面添加也不一定容易)。


3
投票

是的,基本上你可以使用这样的东西:

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

然后

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

对于大文件来说并不是很有效。


3
投票

使用两个流,您可以就地执行此操作,但请记住,这仍然会在每次添加时循环遍历整个文件

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

2
投票

我认为最好的方法是创建一个临时文件。添加文本,然后读取原始文件的内容,将其添加到临时文件中。然后您可以用临时文件覆盖原始文件。


2
投票

前置:

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

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

即使您编写的内容很小,临时文件也会在 .Replace() 之前将整个原始文件附加到其中,因此它确实需要位于磁盘上。

注意这段代码不是线程安全的;如果多个线程访问此代码,您可能会丢失此处正在进行的文件交换中的写入。也就是说,它也相当昂贵,所以无论如何你都想控制对它的访问 - 通过多个 Provider 将写入传递到缓冲区,该缓冲区通过单个 Consumer 线程上的此 prepend 方法定期清空。


1
投票

您应该能够在不打开新文件的情况下执行此操作。使用以下文件方法:

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

确保指定 FileAccess.ReadWrite。

使用从 File.Open 返回的 FileStream,将所有现有数据读入内存。然后将指针重置到文件的开头,写入新数据,然后写入现有数据。

(如果文件很大和/或您怀疑使用了太多内存,您可以执行此操作,而无需将整个文件读入内存,但实现这一点将作为读者的练习。)


1
投票

以下算法可以很容易地解决问题,它对于任何大小的文件(包括非常大的文本文件)都是最有效的:

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

0
投票

使用这个类:

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

0
投票

将文件的内容放入字符串中。将要添加到文件顶部的新数据附加到该字符串 -

string = newdata + string
。然后将文件的查找位置移动到0并将字符串写入文件中。


0
投票

使用 Stack 类(System.Collections.Generic)

Stack<string> Text = new Stack<string>();

void PrependTextToFile(string textToPrepend, string filePath)
{
    Text.Push(textToPrepend);
    File.WriteAllLines(filePath, Text.ToArray());
}

Task PrependTextToFileAsync(string textToPrepend, StorageFile file)
{
    Text.Push(textToPrepend);
    await FileIO.WriteLinesAsync(file, Text);
}    
© www.soinside.com 2019 - 2024. All rights reserved.