使用 C# 将文本添加到文件开头的最佳方法是什么?
我找不到直接的方法来做到这一点,但想出了一些解决方法。
打开新文件,写入我要添加的文本,将旧文件中的文本附加到新文件的末尾。
由于我要添加的文本应该少于200个字符,所以我想我可以在文件的开头添加空白字符,然后用我要添加的文本覆盖空白。
还有其他人遇到过这个问题吗?如果有,你是怎么做的?
这对我有用,但适用于小文件。否则这可能不是一个很好的解决方案。
string currentContent = String.Empty;
if (File.Exists(filePath))
{
currentContent = File.ReadAllText(filePath);
}
File.WriteAllText(filePath, newContent + currentContent );
添加到文件的开头(前置而不是附加)通常不受支持的操作。你的#1选项很好。如果您无法写入临时文件,您可以将整个文件拉入内存,将数据预先添加到字节数组中,然后将其覆盖(只有当您的文件很小并且您不必这样做时,这才真正可行)内存中一次有一堆,因为如果没有副本,在数组前面添加也不一定容易)。
是的,基本上你可以使用这样的东西:
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.");
}
对于大文件来说并不是很有效。
使用两个流,您可以就地执行此操作,但请记住,这仍然会在每次添加时循环遍历整个文件
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);
}
}
}
}
}
我认为最好的方法是创建一个临时文件。添加文本,然后读取原始文件的内容,将其添加到临时文件中。然后您可以用临时文件覆盖原始文件。
前置:
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);
即使您编写的内容很小,临时文件也会在 .Replace() 之前将整个原始文件附加到其中,因此它确实需要位于磁盘上。
注意这段代码不是线程安全的;如果多个线程访问此代码,您可能会丢失此处正在进行的文件交换中的写入。也就是说,它也相当昂贵,所以无论如何你都想控制对它的访问 - 通过多个 Provider 将写入传递到缓冲区,该缓冲区通过单个 Consumer 线程上的此 prepend 方法定期清空。
您应该能够在不打开新文件的情况下执行此操作。使用以下文件方法:
public static FileStream Open(
string path,
FileMode mode,
FileAccess access
)
确保指定 FileAccess.ReadWrite。
使用从 File.Open 返回的 FileStream,将所有现有数据读入内存。然后将指针重置到文件的开头,写入新数据,然后写入现有数据。
(如果文件很大和/或您怀疑使用了太多内存,您可以执行此操作,而无需将整个文件读入内存,但实现这一点将作为读者的练习。)
以下算法可以很容易地解决问题,它对于任何大小的文件(包括非常大的文本文件)都是最有效的:
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());
使用这个类:
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);
}
}
}
}
}
将文件的内容放入字符串中。将要添加到文件顶部的新数据附加到该字符串 -
string = newdata + string
。然后将文件的查找位置移动到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);
}