簡體   English   中英

以編程方式修改文件內容

[英]Modify file content programmatically

我必須使用 C# 以編程方式修改文件中的文本。執行此操作並避免重寫整個文件內容的最佳方法是什么。 我只想編輯一些單詞並保存。

正如其他人所說,您可以輕松地 append 到現有文件。 在文件中間插入數據需要您寫出文件的其余部分。 沒有辦法只移動文件中的所有內容。 如果您想就地覆蓋單個字節,您可以打開一個FileStream 然后,您使用“Seek”到 go 到您要覆蓋的特定字節,並使用 Write 或 WriteByte 將現有數據覆蓋為新數據。

您可以使用 StreamWriter 和 AppendText 輕松地 append 到現有文件。

如果你想做更嚴重的修改,那么我認為你必須使用 StreamReader 將文件的內容讀入 C# 中的 memory,以編程方式修改 stream 的內容,然后用流的內容重寫文件。

我認為無論如何都是如此。

如果您的目標是 windows,則可以使用 winapi CreateFile、ReadFile、WriteFile 等 您可以輕松地寫入文件中的任何位置並寫入任意數量的數據。 您不必重寫該文件。 這甚至不需要不安全的代碼。 我限制了函數的功能(沒有異步或古怪的文件映射)只是為了快速完成這項工作,但它可以毫無問題地讀寫文件。 為了讓這個例子工作,制作一個 100 字節左右的小文本文件,並確保它在 exe 的位置。 此代碼的一小部分來自 MSDN 網站。 不過,我已經對其進行了重大修改。

using System;
using Microsoft.Win32.SafeHandles;
using System.Runtime.InteropServices;
using System.ComponentModel;
using System.Windows.Forms;
using Path = System.IO.Path;

class Program {

  static void Main() {

    string sTestFile = Path.Combine(Path.GetDirectoryName(
     Application.ExecutablePath), "Test.txt");

    // test reading a file
    WinApiFile File = new WinApiFile(sTestFile,
     WinApiFile.DesiredAccess.GENERIC_READ);
    byte[] aBuffer = new byte[1000];
    uint cbRead = File.Read(aBuffer, 1000);
    File.Close();

    // Test writing the file
    File.Open(WinApiFile.DesiredAccess.GENERIC_WRITE);
    // write the time to a 10 byte offset in the file
    // conver the date time to byte array
    int i = 0;
    foreach (var ch in DateTime.Now.ToString()) 
      aBuffer[i++] = (byte)ch;
    // now write it out
    File.MoveFilePointer(10);  // 10 byte offset
    File.Write(aBuffer, (uint)i);  // write the time
    File.Dispose();
  }
}

public class WinApiFile : IDisposable {

  /* ---------------------------------------------------------
   * private members
   * ------------------------------------------------------ */
  private SafeFileHandle _hFile = null;
  private string _sFileName = "";
  private bool _fDisposed;

  /* ---------------------------------------------------------
   * properties
   * ------------------------------------------------------ */
  public bool IsOpen { get { return (_hFile != null); } }
  public SafeFileHandle Handle { get { return _hFile; } }
  public string FileName {
    get { return _sFileName; }
    set {
      _sFileName = (value ?? "").Trim();
      if (_sFileName.Length == 0)
        CloseHandle(_hFile);
    }
  }
  public int FileLength {
    get {
      return (_hFile != null) ? (int)GetFileSize(_hFile,
       IntPtr.Zero) : 0;
    }
    set {
      if (_hFile == null)
        return;
      MoveFilePointer(value, MoveMethod.FILE_BEGIN);
      if (!SetEndOfFile(_hFile))
        ThrowLastWin32Err();
    }
  }

  /* ---------------------------------------------------------
   * Constructors
   * ------------------------------------------------------ */

  public WinApiFile(string sFileName,
   DesiredAccess fDesiredAccess) {
    FileName = sFileName;
    Open(fDesiredAccess);
  }
  public WinApiFile(string sFileName,
   DesiredAccess fDesiredAccess,
   CreationDisposition fCreationDisposition) {
    FileName = sFileName;
    Open(fDesiredAccess, fCreationDisposition);
  }

  /* ---------------------------------------------------------
   * Open/Close
   * ------------------------------------------------------ */

  public void Open(
   DesiredAccess fDesiredAccess) {
    Open(fDesiredAccess, CreationDisposition.OPEN_EXISTING);
  }

  public void Open(
   DesiredAccess fDesiredAccess,
   CreationDisposition fCreationDisposition) {
    ShareMode fShareMode;
    if (fDesiredAccess == DesiredAccess.GENERIC_READ) {
      fShareMode = ShareMode.FILE_SHARE_READ;
    } else {
      fShareMode = ShareMode.FILE_SHARE_NONE;
    }
    Open(fDesiredAccess, fShareMode, fCreationDisposition, 0);
  }

  public void Open(
   DesiredAccess fDesiredAccess, 
   ShareMode fShareMode, 
   CreationDisposition fCreationDisposition, 
   FlagsAndAttributes fFlagsAndAttributes) {

    if (_sFileName.Length == 0)
      throw new ArgumentNullException("FileName");
    _hFile = CreateFile(_sFileName, fDesiredAccess, fShareMode, 
     IntPtr.Zero, fCreationDisposition, fFlagsAndAttributes, 
     IntPtr.Zero);
    if (_hFile.IsInvalid) {
      _hFile = null;
      ThrowLastWin32Err();
    }
    _fDisposed = false;

  }

  public void Close() {
    if (_hFile == null)
      return;
    _hFile.Close();
    _hFile = null;
    _fDisposed = true;
  }

  /* ---------------------------------------------------------
   * Move file pointer 
   * ------------------------------------------------------ */

  public void MoveFilePointer(int cbToMove) {
    MoveFilePointer(cbToMove, MoveMethod.FILE_CURRENT);
  }

  public void MoveFilePointer(int cbToMove, 
   MoveMethod fMoveMethod) {
    if (_hFile != null)
      if (SetFilePointer(_hFile, cbToMove, IntPtr.Zero, 
       fMoveMethod) == INVALID_SET_FILE_POINTER)
        ThrowLastWin32Err();
  }

  public int FilePointer {
    get {
      return (_hFile != null) ? (int)SetFilePointer(_hFile, 0, 
       IntPtr.Zero, MoveMethod.FILE_CURRENT) : 0;
    }
    set {
      MoveFilePointer(value);
    }
  }

  /* ---------------------------------------------------------
   * Read and Write
   * ------------------------------------------------------ */

  public uint Read(byte[] buffer, uint cbToRead) {
    // returns bytes read
    uint cbThatWereRead = 0;
    if (!ReadFile(_hFile, buffer, cbToRead, 
     ref cbThatWereRead, IntPtr.Zero))
      ThrowLastWin32Err();
    return cbThatWereRead;
  }

  public uint Write(byte[] buffer, uint cbToWrite) {
    // returns bytes read
    uint cbThatWereWritten = 0;
    if (!WriteFile(_hFile, buffer, cbToWrite, 
     ref cbThatWereWritten, IntPtr.Zero))
      ThrowLastWin32Err();
    return cbThatWereWritten;
  }

  /* ---------------------------------------------------------
   * IDisposable Interface
   * ------------------------------------------------------ */
  public void Dispose() {
    Dispose(true);
    GC.SuppressFinalize(this);
  }

  protected virtual void Dispose(bool fDisposing) {
    if (!_fDisposed) {
      if (fDisposing) {
        if (_hFile != null)
          _hFile.Dispose();
        _fDisposed = true;
      }
    }
  }

  ~WinApiFile() {
    Dispose(false);
  }

  /* ---------------------------------------------------------
   * WINAPI STUFF
   * ------------------------------------------------------ */

  private void ThrowLastWin32Err() {
    Marshal.ThrowExceptionForHR(
     Marshal.GetHRForLastWin32Error());
  }

  [Flags]
  public enum DesiredAccess : uint {
    GENERIC_READ = 0x80000000,
    GENERIC_WRITE = 0x40000000
  }
  [Flags]
  public enum ShareMode : uint {
    FILE_SHARE_NONE = 0x0,
    FILE_SHARE_READ = 0x1,
    FILE_SHARE_WRITE = 0x2,
    FILE_SHARE_DELETE = 0x4,

  }
  public enum MoveMethod : uint {
    FILE_BEGIN = 0,
    FILE_CURRENT = 1,
    FILE_END = 2
  }
  public enum CreationDisposition : uint {
    CREATE_NEW = 1,
    CREATE_ALWAYS = 2,
    OPEN_EXISTING = 3,
    OPEN_ALWAYS = 4,
    TRUNCATE_EXSTING = 5
  }
  [Flags]
  public enum FlagsAndAttributes : uint {
    FILE_ATTRIBUTES_ARCHIVE = 0x20,
    FILE_ATTRIBUTE_HIDDEN = 0x2,
    FILE_ATTRIBUTE_NORMAL = 0x80,
    FILE_ATTRIBUTE_OFFLINE = 0x1000,
    FILE_ATTRIBUTE_READONLY = 0x1,
    FILE_ATTRIBUTE_SYSTEM = 0x4,
    FILE_ATTRIBUTE_TEMPORARY = 0x100,
    FILE_FLAG_WRITE_THROUGH = 0x80000000,
    FILE_FLAG_OVERLAPPED = 0x40000000,
    FILE_FLAG_NO_BUFFERING = 0x20000000,
    FILE_FLAG_RANDOM_ACCESS = 0x10000000,
    FILE_FLAG_SEQUENTIAL_SCAN = 0x8000000,
    FILE_FLAG_DELETE_ON = 0x4000000,
    FILE_FLAG_POSIX_SEMANTICS = 0x1000000,
    FILE_FLAG_OPEN_REPARSE_POINT = 0x200000,
    FILE_FLAG_OPEN_NO_CALL = 0x100000
  }

  public const uint INVALID_HANDLE_VALUE = 0xFFFFFFFF;
  public const uint INVALID_SET_FILE_POINTER = 0xFFFFFFFF;
  // Use interop to call the CreateFile function.
  // For more information about CreateFile,
  // see the unmanaged MSDN reference library.
  [DllImport("kernel32.dll", SetLastError = true)]
  internal static extern SafeFileHandle CreateFile(
   string lpFileName,
   DesiredAccess dwDesiredAccess, 
   ShareMode dwShareMode, 
   IntPtr lpSecurityAttributes,
   CreationDisposition dwCreationDisposition, 
   FlagsAndAttributes dwFlagsAndAttributes,
   IntPtr hTemplateFile);

  [DllImport("kernel32", SetLastError = true)]
  internal static extern Int32 CloseHandle(
   SafeFileHandle hObject);

  [DllImport("kernel32", SetLastError = true)]
  internal static extern bool ReadFile(
   SafeFileHandle hFile, 
   Byte[] aBuffer,
   UInt32 cbToRead,
   ref UInt32 cbThatWereRead, 
   IntPtr pOverlapped);

  [DllImport("kernel32.dll", SetLastError = true)]
  internal static extern bool WriteFile(
   SafeFileHandle hFile, 
   Byte[] aBuffer,
   UInt32 cbToWrite, 
   ref UInt32 cbThatWereWritten, 
   IntPtr pOverlapped);

  [DllImport("kernel32.dll", SetLastError = true)]
  internal static extern UInt32 SetFilePointer(
   SafeFileHandle hFile,
   Int32 cbDistanceToMove, 
   IntPtr pDistanceToMoveHigh,
   MoveMethod fMoveMethod);

  [DllImport("kernel32.dll", SetLastError = true)]
  internal static extern bool SetEndOfFile(
   SafeFileHandle hFile);

  [DllImport("kernel32.dll", SetLastError = true)]
  internal static extern UInt32 GetFileSize(
   SafeFileHandle hFile, 
   IntPtr pFileSizeHigh);

}

當您打開文件進行寫入時,只有兩種可能的結果。 要么打開它進行附加,這將允許您只在末尾寫入,要么打開它進行正常寫入,在這種情況下,您將從文件的開頭寫入。 沒有“在中點打開,寫入然后根據插入長度移動剩余內容”。

某些 API 可能會允許這樣的修改,但在幕后,它們只允許您執行這兩個基本操作。 你不應該太擔心重寫整個文件,但是,除非你的文件很大並且你的硬盤驅動器非常慢,否則它並不是一個糟糕的操作。 相反,您應該更擔心這樣做的次數。

  1. 打開文件
  2. 將整個文件讀入變量或數組
  3. 插入修改
  4. 回寫文件

讀入文件並修改它然后再次讀出它可能更簡單。

為了真正高效(即不是為每個操作讀取/寫入整個文件),您需要做一些內務處理以跟蹤有關文件的元數據,然后進行二進制編輯。

您仍然需要管理文件增長和需要附加的情況等。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM