FileOpen, FileRead, FileWrite

前端 未结 5 1473
孤街浪徒
孤街浪徒 2021-01-05 18:22

How to properly work with FileRead, FileWrite, buffers (or with TFileStream).

I need to read a whole text file to a String, then to write back a String to this file

相关标签:
5条回答
  • 2021-01-05 19:00

    Here are two functions doing what you want:

    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;
    

    They use low-level FileOpen/FIleSeek/FileRead/FileWrite functions.

    And you can specify any fmShare* option you need.

    It uses a RawByteString type, so it expects the text to be handled in a bytes-oriented way. It won't work with Unicode text file, but with Ansi Text. You'll have to set the appropriate code page if you want to interact with it using the string type since Delphi 2009.

    Before Delphi 2009, just define:

    type
      RawByteString = AnsiString;
    
    0 讨论(0)
  • 2021-01-05 19:03

    Added requested example of working with FileXXX function family too. Note on using RawByteString and file I/O - it is perfectly valid. Another note: for brevity in the low-level code i omitted some error-checking assertions for less likely occurring errors. Caveat emptor!

    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;
    
    0 讨论(0)
  • 2021-01-05 19:07

    TStringList is what you want if you need to deal with the file on a per-line basis.

    If you just want to treat it as a single string blob then there is 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;
    
    0 讨论(0)
  • 2021-01-05 19:12

    The simplest, most fool-proof way to read a file into a string is to use a TStringList like so:

    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;
    

    As has been noted in the comments below your question, this could end up transforming line breaks within your string - so depending on what you're trying to read / do, you will need to look out for this.

    0 讨论(0)
  • 2021-01-05 19:17

    Some of my utility routines (you can download the code in full from my web site)...

    //------------------------------------------------------------------------------
    // 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;
    
    0 讨论(0)
提交回复
热议问题