简体   繁体   English

以编程方式修改文件内容

[英]Modify file content programmatically

I have to modify a text in a file programmatically with C#. What's the best way to do this and avoid rewriting the entire file content.我必须使用 C# 以编程方式修改文件中的文本。执行此操作并避免重写整个文件内容的最佳方法是什么。 I want to edit only some words and save it.我只想编辑一些单词并保存。

As others have said, you can easily append to an existing file.正如其他人所说,您可以轻松地 append 到现有文件。 Inserting data in the middle of the file requires that you write out the remainder of the file.在文件中间插入数据需要您写出文件的其余部分。 There's no way to just shift everything in the file.没有办法只移动文件中的所有内容。 If you want to overwrite individual bytes in place, you can open a FileStream .如果您想就地覆盖单个字节,您可以打开一个FileStream You then use the "Seek" to go to the specific bytes you want to overwrite, and use Write, or WriteByte to overwrite the existing data with new data.然后,您使用“Seek”到 go 到您要覆盖的特定字节,并使用 Write 或 WriteByte 将现有数据覆盖为新数据。

You can append to an existing file using StreamWriter and AppendText easily.您可以使用 StreamWriter 和 AppendText 轻松地 append 到现有文件。

If you want to do more serious modifications, then I think you must read the contents of the file into memory in C# using StreamReader for example, modifying the contents programmatically of that stream, and then re-writing the file with the stream's contents.如果你想做更严重的修改,那么我认为你必须使用 StreamReader 将文件的内容读入 C# 中的 memory,以编程方式修改 stream 的内容,然后用流的内容重写文件。

I think that's how it is anyway.我认为无论如何都是如此。

If you are targeting windows, you can use the winapi CreateFile, ReadFile, WriteFile, etc .如果您的目标是 windows,则可以使用 winapi CreateFile、ReadFile、WriteFile 等 You can easily write to any spot in a file and write as much data as you want.您可以轻松地写入文件中的任何位置并写入任意数量的数据。 You do not have to rewrite the file.您不必重写该文件。 This doesn't even require unsafe code.这甚至不需要不安全的代码。 I limited the functionality of the functions (no asynch or wacky file mappings) just to get this done quickly, but it can read and write files no problem.我限制了函数的功能(没有异步或古怪的文件映射)只是为了快速完成这项工作,但它可以毫无问题地读写文件。 To get this example to work, make a small text file 100 bytes or so, and make sure it is in the location of the exe.为了让这个例子工作,制作一个 100 字节左右的小文本文件,并确保它在 exe 的位置。 A little bit of this code comes from the MSDN website.此代码的一小部分来自 MSDN 网站。 I have modified it significantly though.不过,我已经对其进行了重大修改。

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

}

When you open a file for writing there are only two possible outcomes.当您打开文件进行写入时,只有两种可能的结果。 Either you open it for appending which will allow you to write at the end only, or you open it for normal write in which case you will write from the beginning of the file.要么打开它进行附加,这将允许您只在末尾写入,要么打开它进行正常写入,在这种情况下,您将从文件的开头写入。 There is no "open at mid point, write then shift remaining content according to insertion length".没有“在中点打开,写入然后根据插入长度移动剩余内容”。

It is possible that some API will allow such modifications but behind the scenes, they will only allow you those two basic operations.某些 API 可能会允许这样的修改,但在幕后,它们只允许您执行这两个基本操作。 You shouldn't worry to much about rewriting the whole file however, it is not such a bad operation unless your file is huge and your hard drive is very slow.你不应该太担心重写整个文件,但是,除非你的文件很大并且你的硬盘驱动器非常慢,否则它并不是一个糟糕的操作。 You should worry more about the number of times you do it instead.相反,您应该更担心这样做的次数。

  1. Open the file打开文件
  2. Read the whole file into a variable or array将整个文件读入变量或数组
  3. Insert modifications插入修改
  4. Write back the file回写文件

It's probably simpler to read the file in and modify it then read it out again.读入文件并修改它然后再次读出它可能更简单。

To really be efficient (ie not read/write the whole file for every operation) you need to do some house keeping to keep track of metadata about the file, and then do binary editing.为了真正高效(即不是为每个操作读取/写入整个文件),您需要做一些内务处理以跟踪有关文件的元数据,然后进行二进制编辑。

You'll still have to manage the case where the file grows and needs to be appended, etc.您仍然需要管理文件增长和需要附加的情况等。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM