【问题标题】:FileOpen, FileRead, FileWrite文件打开、文件读取、文件写入
【发布时间】:2011-06-18 17:46:08
【问题描述】:

如何正确使用 FileRead、FileWrite、缓冲区(或 TFileStream)。

我需要将整个文本文件读入一个字符串,然后将一个字符串写回这个文件(用一个新字符串替换它)。

【问题讨论】:

  • 您确定要将整个文本文件写入字符串吗?我会使用 TStringList 或 -- 如果您更喜欢更基本的方法 -- 字符串数组。
  • 我会使用 FileStream。特别是在 D2009 或更高版本上,因为 FileRead 和相关方法不支持 Unicode。
  • @Marjan:这完全取决于您如何定义“支持”。使用旧的 Pascal I/O 读取和写入 UTF-8 文件一点也不难,例如在 Delphi 2009+ 中。
  • @Andreas:是的,您可以进行设置,以便发送读/写(Ln)正确的字节值以放入文件中,但这不是我所说的支持。尝试移植一个简单​​的打开,使用 WriteLn(somestring, somestring) 写入一些行,关闭其中 somestring 现在可能包含非 ASCII 并且您不走运。此外,Embarcadero 不鼓励使用“旧”例程,并建议使用 Streams 进行所有与文件相关的字符串操作。
  • @maxfax 问题没有提到共享模式

标签: file delphi buffer


【解决方案1】:
如果您需要逐行处理文件,

TStringList 就是您想要的。

如果您只想将其视为单个字符串 blob,则可以使用 TStringStream

Stream := TStringStream.Create('', TEncoding.UTF8);
Try
  Stream.LoadFromFile('c:\desktop\in.txt');
  ShowMessage(Stream.DataString);

  Stream.Clear;
  Stream.WriteString('Greetings earthlings!');
  Stream.SaveToFile('c:\desktop\out.txt');
Finally
  Stream.Free;
End;

【讨论】:

  • +1 个很好的例子,说明这一切通过流变得多么简单。
【解决方案2】:

将文件读入字符串的最简单、最简单的方法是使用 TStringList,如下所示:

sl := TStringList.Create;
try
    sl.LoadFromFile('C:\myfile.txt');

    //String can be read and modified using the Text property:
    oldString := sl.Text;
    sl.Text := 'foo bar';

    //Text can be written back to the file using:
    sl.WriteToFile('C:\myfile.txt');
finally
    sl.Free;
end;

正如您在问题下方的 cmets 中所指出的,这最终可能会在您的字符串中转换换行符 - 因此,根据您尝试阅读/执行的操作,您需要注意这一点。

【讨论】:

  • 你可能想说一些关于编码的事情;我相信您的示例将文件保存在 ANSI 中
【解决方案3】:

我的一些实用程序(您可以从我的网站下载完整的代码)...

//------------------------------------------------------------------------------
// CsiStrToBytes
//
// Convert pInStr to an array of bytes using the string encoding
// pStringEncoding (one of automatic, Ansi, UTF-16, or UTF-8) and optionally
// include the byte order mark according to the pIncludeBom flag
//------------------------------------------------------------------------------
function CsiStrToBytes(const pInStr: string;
                       pStringEncoding: TECsiStringEncoding;
                       pIncludeBom: Boolean): TByteDynArray;
var
{$IFDEF UNICODE}
  lStringEncoding: TECsiStringEncoding;
  lStringStream: TStringStream;
  lPreambleBytes: TBytes;
  lStringBytes: TBytes;
  lPreambleLen: Integer;
  lStringLen: Integer;
{$ENDIF}
  lLen: Integer;
{$IFDEF UNICODE}
  lIndex: Integer;
{$ENDIF}
begin
  if pInStr <> '' then begin
{$IFDEF UNICODE}
    if pStringEncoding = seAuto then
      lStringEncoding := CsiGetPreferredEncoding(pInStr)
    else
      lStringEncoding := pStringEncoding;

    // UTF-8 and UTF-16 encoding can be handled by the TStringStream class
    if (lStringEncoding = seUtf8) or (lStringEncoding = seUtf16) then begin
      if lStringEncoding = seUtf8 then
        lStringStream := TStringStream.Create(pInStr, TEncoding.Utf8)
      else
        lStringStream := TStringStream.Create(pInStr, TEncoding.Unicode);
      try
        // add the UTF-8 or UTF-16 byte order mark to the start of the array of
        // bytes if required
        if pIncludeBom then
          lPreambleBytes := lStringStream.Encoding.GetPreamble
        else
          SetLength(lPreambleBytes, 0);
        lStringBytes := lStringStream.Bytes;
        lPreambleLen := Length(lPreambleBytes);
        lStringLen := Length(lStringBytes);
        SetLength(Result, lPreambleLen + lStringLen);
        if lPreambleLen > 0 then
          Move(lPreambleBytes[0], Result[0], lPreambleLen);
        if lStringLen > 0 then
          Move(lStringBytes[0], Result[lPreambleLen], lStringLen);
      finally
        lStringStream.Free;
      end;

    end else begin
{$ENDIF}
      // Ansi encoding must be handled manually
      lLen := Length(pInStr);
      SetLength(Result, lLen);
{$IFDEF UNICODE}
      for lIndex := 1 to lLen do
        Result[lIndex - 1] := Ord(pInStr[lIndex]) and $00ff;
{$ELSE}
      Move(pInStr[1], Result[0], lLen);
{$ENDIF}
{$IFDEF UNICODE}
    end;
{$ENDIF}

  end else
    SetLength(Result, 0);
end;

//------------------------------------------------------------------------------
// CsiSaveToFile
//
// Saves pData, an array of bytes, to pFileName
//------------------------------------------------------------------------------
procedure CsiSaveToFile(const pData: TByteDynArray; const pFileName: string);
var
  lFileStream: TFileStream;
  lLen: Integer;
begin
  lFileStream := TFileStream.Create(pFileName, fmCreate);
  try
    lLen := Length(pData);
    if lLen > 0 then
      lFileStream.WriteBuffer(pData[0], lLen);
  finally
    lFileStream.Free;
  end;
end;

//------------------------------------------------------------------------------
// CsiSaveToFile
//
// Saves pText to pFileName using the string encoding pStringEncoding, which is
// one of automatic, Ansi, UTF-16, or UTF-8
//------------------------------------------------------------------------------
procedure CsiSaveToFile(const pText: string; const pFileName: string;
                        pStringEncoding: TECsiStringEncoding);
begin
  CsiSaveToFile(CsiStrToBytes(pText, pStringEncoding), pFileName);
end;

【讨论】:

    【解决方案4】:

    这里有两个函数可以做你想做的事情:

    function StringFromFile(const FileName: TFileName): RawByteString;
    var F: THandle;
        Size: integer;
    begin
      result := '';
      if FileName='' then
        exit;
      F := FileOpen(FileName,fmOpenRead or fmShareDenyNone);
      if PtrInt(F)>=0 then begin
    {$ifdef LINUX}
        Size := FileSeek(F,0,soFromEnd);
        FileSeek(F,0,soFromBeginning);
    {$else}
        Size := GetFileSize(F,nil);
    {$endif}
        SetLength(result,Size);
        if FileRead(F,pointer(Result)^,Size)<>Size then
          result := '';
        FileClose(F);
      end;
    end;
    
    function FileFromString(const Content: RawByteString; const FileName: TFileName;
      FlushOnDisk: boolean=false): boolean;
    var F: THandle;
        L: integer;
    begin
      result := false;
      F := FileCreate(FileName);
      if PtrInt(F)<0 then
        exit;
      if pointer(Content)<>nil then
        L := FileWrite(F,pointer(Content)^,length(Content)) else
        L := 0;
      result := (L=length(Content));
    {$ifdef MSWINDOWS}
      if FlushOnDisk then
        FlushFileBuffers(F);
    {$endif}
      FileClose(F);
    end;
    

    他们使用低级 FileOpen/FIleSeek/FileRead/FileWrite 函数。

    您可以指定任何您需要的fmShare* 选项。

    它使用RawByteString 类型,因此它希望以面向字节的方式处理文本。它不适用于 Unicode 文本文件,但适用于 Ansi 文本。自 Delphi 2009 以来,如果您想使用字符串类型与之交互,则必须设置适当的代码页。

    在 Delphi 2009 之前,只需定义:

    type
      RawByteString = AnsiString;
    

    【讨论】:

    • 我很想投-1,因为您没有发送 GetFileSize 的代码。但我用你的linux计算来获取文件大小,所以投票+1。谢谢
    【解决方案5】:

    还添加了使用 FileXXX 函数系列的请求示例。 注意使用 RawByteString 和文件 I/O - 它是完全有效的。 另一个注意事项:为了在低级代码中简洁起见,我省略了一些错误检查断言,以便不太可能发生错误。警告购买者!

    const
      FileName = 'Unit14.pas';  // this program is a stuntmaster,
                                // reads and writes its own source
    
    procedure TForm14.FormClick(Sender: TObject);
    var
      Stream: TFileStream;
      Buffer: RawByteString;
    begin
      Stream := TFileStream.Create(FileName, fmOpenReadWrite or fmShareExclusive);
      // read entire file into string buffer
      SetLength(Buffer, Stream.Size);
      Stream.ReadBuffer(Buffer[1], Stream.Size);
    
      // do something with string
      OutputDebugString(PChar(Format('Buffer = "%s"', [Buffer])));
    
      // prepare to write
      Stream.Position := 0;   // rewind file pointer
      Stream.Size := 0;       // truncate the file
    
      // write entire string into the file
      Stream.WriteBuffer(Buffer[1], Length(Buffer));
    
      Stream.Free;
    end;
    
    // on right click - do exactly the same but using low-level FileXXX calls
    procedure TForm14.FormContextPopup(Sender: TObject; MousePos: TPoint; var
        Handled: Boolean);
    var
      Handle: Integer;
      Size: Cardinal;
      Buffer: RawByteString;
      Transferred: Integer;
    begin
      Handle := FileOpen(FileName, fmOpenReadWrite or fmShareExclusive);
      Assert(Handle >= 0);
    
      // read entire file into string buffer
      Size := GetFileSize(Handle, nil);
      Assert(Size <> INVALID_FILE_SIZE);
      SetLength(Buffer, Size);
      Transferred := FileRead(Handle, Buffer[1], Size);
      Assert(not (Transferred < Size));
    
      // do something with string
      OutputDebugString(PChar(Format('Buffer = "%s"', [Buffer])));
    
      // prepare to write
      FileSeek(Handle, 0, 0); // rewind file pointer
      SetEndOfFile(Handle);   // truncate the file
    
      // write entire string into the file
      Transferred := FileWrite(Handle, Buffer[1], Length(Buffer));
      Assert(not (Transferred < Length(Buffer)));
    
      FileClose(Handle);
    end;
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2020-05-13
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2017-08-26
      相关资源
      最近更新 更多