2009-08-27 4 views
26

Что такое лучший способ добавить текст в начало файла с помощью C#?C#: предварительная подготовка к началу файла

Я не мог найти простой способ сделать это, но придумал пару обходов.

  1. Откройте новый файл, написать текст, который я хотел бы добавить, добавить текст из старого файла в конце нового файла.

  2. Поскольку текст, который я хочу добавить, должен быть меньше 200 символов, я думал, что могу добавить символы пробела в начало файла, а затем переписать пробел с текстом, который я хотел добавить.

Неужели кто-то еще сталкивается с этой проблемой, и если да, то что вы сделали?

+1

Интересный вопрос. Очевидно, что фреймворк (и, без сомнения, базовая файловая система) является привязанным к приложению, поскольку файлы File.AppendText и File.AppendAllText предоставляются без соответствующих методов добавления. Делает смысл - байты могут быть добавлены без «перемещения» существующего контента. – harpo

+0

hmm ok, поэтому ответы кажутся предпочтительными для варианта 1 (либо с использованием файла temp, либо чтения в память). Есть ли недостаток с использованием опции 2? – Grace

+0

Вы можете проверить это http://stackoverflow.com/questions/1008742/adding-text-to-beginning-and-end-of-file-in-c сообщение о добавлении текста в начало и конец файла в C#. Его для файлов XML, но легко модифицируется для вашего использования. – SwDevMan81

ответ

9

Добавление в начало файла (добавление в отличие от добавления) обычно не поддерживается. Ваши # 1 варианты в порядке. Если вы не можете написать временный файл, вы можете вытащить весь файл в память, предварите свои данные в массив байтов и затем перезапишите его обратно (это реально реально, если ваши файлы невелики, и вам не нужно имеют кучу в памяти сразу, потому что добавление массива не обязательно легко без копирования).

+2

О, верно, "preend." Благодаря! Я не мог придумать правильное слово. – Grace

2

Я думаю, что лучший способ - создать временный файл. Добавьте текст, затем прочитайте содержимое исходного файла, добавив его в файл temp. Затем вы можете перезаписать оригинал временным файлом.

+8

Не перезаписывайте исходный файл, который удаляет ACL и ADS. Используйте File.Replace. –

+0

Я знаю, что это уже давно. ACL = Список контроля доступа? ADS =? – GibralterTop

1

Вы должны быть в состоянии сделать это, не открывая новый файл. Используйте следующий способ:

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

Обязательно укажите FileAccess.ReadWrite.

Используя FileStream, возвращенный из File.Open, прочитайте все существующие данные в памяти. Затем сбросьте указатель на начало файла, напишите новые данные, а затем запишите существующие данные.

(Если файл большой и/или вы подозреваете, что используете слишком много памяти, вы можете сделать это, не прочитав весь файл в памяти, но его реализация останется в качестве упражнения для читателя.)

0

Поместите содержимое файла в строку. Добавьте новые данные, которые вы хотите добавить в верхнюю часть файла, к этой строке - string = newdata + string. Затем переместите позицию поиска файла в 0 и напишите строку в файл.

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

Даже если то, что вы пишете небольшой файл Temp получает весь исходный файл, присоединенный к нему перед .Привернуть(), поэтому он должен быть на диске.

Обратите внимание, что этот код не является потокобезопасным; если более чем один поток обращается к этому коду, вы можете потерять записи в процессе обмена файлами. Тем не менее, это тоже довольно дорого, поэтому вам все равно нужен доступ к шлюзу - передайте записи через несколько провайдеров в буфер, который периодически освобождается через этот метод prepend на одном потоке Consumer.

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

не очень эффективным, хотя в случае больших файлов.

24

Это работает для меня, но для небольших файлов. Наверное, это не очень хорошее решение.

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

это то, что я сделал бы также .. легкий, грязный и простой ... – SolidSnake

1

Используйте этот класс:

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

перед именем:

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

, используя два потока, вы можете сделать это на месте, но имейте в виду, что это все равно будет охватывать весь файл при каждом добавлении

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); 
       } 
      } 
     } 
    } 
} 
Смежные вопросы