繁体   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