FileOpen, FileRead, FileWrite - PullRequest
       26

FileOpen, FileRead, FileWrite

5 голосов
/ 18 июня 2011

Как правильно работать с FileRead, FileWrite, буферами (или с TFileStream).

Мне нужно прочитать весь текстовый файл в строку, а затем записать обратно строку в этот файл (заменить ее новой строкой).

Ответы [ 5 ]

11 голосов
/ 18 июня 2011

TStringList - это то, что вам нужно, если вам нужно работать с файлом на отдельной строке.

Если вы просто хотите рассматривать его как однорядный блоб, тогда есть 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;
5 голосов
/ 18 июня 2011

Самый простой и надежный способ прочитать файл в строку - это использовать 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;

Как было отмечено в комментариях под вашим вопросом, это может закончитьсяпреобразование разрывов строк в вашей строке - поэтому, в зависимости от того, что вы пытаетесь прочитать / сделать, вам нужно следить за этим.

2 голосов
/ 19 июня 2011

Вот две функции, которые делают то, что вы хотите:

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 Text.Вам придется установить соответствующую кодовую страницу, если вы хотите взаимодействовать с ней, используя строковый тип начиная с Delphi 2009.

До Delphi 2009 просто определите:

type
  RawByteString = AnsiString;
2 голосов
/ 19 июня 2011

Некоторые из моих утилит (вы можете скачать код полностью с моего сайта) ...

//------------------------------------------------------------------------------
// 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;
1 голос
/ 18 июня 2011

Добавлен запрошенный пример работы с семейством функций FileXXX. Замечание по использованию RawByteString и файловому вводу / выводу - это совершенно правильно. Еще одно примечание: для краткости в низкоуровневом коде я пропустил некоторые утверждения проверки ошибок для менее вероятных ошибок. Будьте бдительны!

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;
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...