2012-01-31 15 views
11

Qualcuno sa come associare un descrittore di file (testo) a un componente TStream, in modo che writeln() come I/O possa essere reindirizzato allo stream? (come l'unità FPC StreamIO). C'è una funzione predefinita da qualche parte (sto usando XE, ma sarebbe bello se funzionasse anche nel 2009)Writeln to stream

Ho un sacco di codice aziendale che si basa sulle opzioni di formattazione come writeln (f,) che Vorrei aggiornare per accedere alla rete. Questo aggiornamento deve essere eseguito in un modo relativamente sicuro, poiché i file devono rimanere uguali al byte.

(riscrittura di questo codice affari con altri mezzi non è davvero un'opzione, se non esiste dovrò provare me stesso, o avrà a che fare con una scrittura a un file temporaneo e la lettura indietro)

Aggiunto: qualsiasi esempio di textrec personalizzato sarebbe benvenuto e/o quale dei suoi campi dispone di spazio sicuro per lo stato dell'utente.

risposta

10

Pietro Di seguito ha scritto una bestia per Delphi troppo, chiamato anche StreamIO, vedere http://groups.google.com/group/borland.public.delphi.objectpascal/msg/d682a8b5a5760ac4?pli=1

(posta collegato contiene l'unità).

+1

+1 bella cattura. Immagino che l'unità FPC StreamIO sia per lo più la stessa ... Ma non sono sicuro che gestirà il testo Unicode. Probabilmente sarai stucked con il tipo di testo Ansi quando usi Writeln(). E non dimenticare di impostare {$ I-} per un processo molto più veloce, se sei sicuro che il tuo TStream di destinazione non fallirà. –

+0

Nome della stessa unità, stesso nome della funzione, prendiamo questo, grazie mille :-) –

+0

Potete per favore fornire qualche esempio su come utilizzare questa unità. – Branko

3

Potete dare un'occhiata al nostro SynCrtSock Open Source unit.

Implementa molte funzionalità (incluso un server HTTP/1.1 basato su HTTP.sys), ma ha anche alcuni file di testo virtuali da scrivere in un socket. È usato per es. implementare un client o server HTTP o SMTP (per inviare un messaggio di posta elettronica).

Sarà un buon esempio di come creare un "virtuale" TTextRec, compresa la lettura del contenuto di scrittura & e anche la gestione degli errori. Anche la dimensione del buffer interno viene migliorata rispetto al suo valore predefinito: qui hai 1KB di cache per impostazione predefinita, invece di 128 byte.

Ad esempio, ecco come può essere utilizzato per inviare una richiesta tramite SMTP (codice sorgente estratta dall'unità):

function SendEmail(const Server: AnsiString; const From, CSVDest, Subject, Text: TSockData; 
    const Headers: TSockData=''; const User: TSockData=''; const Pass: TSockData=''; 
    const Port: AnsiString='25'): boolean; 
var TCP: TCrtSocket; 
procedure Expect(const Answer: TSockData); 
var Res: TSockData; 
begin 
    repeat 
    readln(TCP.SockIn^,Res); 
    until (Length(Res)<4)or(Res[4]<>'-'); 
    if not IdemPChar(pointer(Res),pointer(Answer)) then 
    raise Exception.Create(string(Res)); 
end; 
procedure Exec(const Command, Answer: TSockData); 
begin 
    writeln(TCP.SockOut^,Command); 
    Expect(Answer) 
end; 
var P: PAnsiChar; 
    rec, ToList: TSockData; 
begin 
    result := false; 
    P := pointer(CSVDest); 
    if P=nil then exit; 
    TCP := Open(Server, Port); 
    if TCP<>nil then 
    try 
    TCP.CreateSockIn; // we use SockIn and SockOut here 
    TCP.CreateSockOut; 
    Expect('220'); 
    if (User<>'') and (Pass<>'') then begin 
     Exec('EHLO '+Server,'25'); 
     Exec('AUTH LOGIN','334'); 
     Exec(Base64Encode(User),'334'); 
     Exec(Base64Encode(Pass),'235'); 
    end else 
     Exec('HELO '+Server,'25'); 
    writeln(TCP.SockOut^,'MAIL FROM:<',From,'>'); Expect('250'); 
    ToList := 'To: '; 
    repeat 
     rec := trim(GetNextItem(P)); 
     if rec='' then continue; 
     if pos(TSockData('<'),rec)=0 then 
     rec := '<'+rec+'>'; 
     Exec('RCPT TO:'+rec,'25'); 
     ToList := ToList+rec+', '; 
    until P=nil; 
    Exec('DATA','354'); 
    writeln(TCP.SockOut^,'Subject: ',Subject,#13#10, 
     ToList,#13#10'Content-Type: text/plain; charset=ISO-8859-1'#13#10+ 
     'Content-Transfer-Encoding: 8bit'#13#10, 
     Headers,#13#10#13#10,Text); 
    Exec('.','25'); 
    writeln(TCP.SockOut^,'QUIT'); 
    result := true; 
    finally 
    TCP.Free; 
    end; 
end; 

Si produrrà solo Ansi contenuti, per definizione.

Si rivolge a Delphi da 5 a XE2, quindi includerà Delphi 2009 o XE.

+0

+1 Bello anche perché mostra le altre funzioni. Non sembra sicuro a 64 bit. Su * nix handle è a 32 bit e un puntatore non si adatta a questo. –

+0

Hmm, al secondo controllo, Delphi lo definisce come THandle. Un tipo di windows, e non so come lo definiscano su * nix. –

1

Ho postato questo in risposta a un'altra domanda, e sembra essere un approccio che vale la pena considerare anche se si desidera scrivere WriteLn (F, qualsiasi, numero, di parametri), e non posso purtroppo imitare esattamente WriteLn(F, ...), con il mio metodo WriteLine(aString).

  1. Desidero utilizzare ReadLn e WriteLn, ma su flussi. Purtroppo non posso supportare parametri arbitrari in WriteLn, ma posso scrivere una stringa, che in combinazione con Format() è abbastanza per me. ie object.WriteLine(Format('stuff %d',[aIntValue]))

  2. Voglio poter leggere qualsiasi file che potrebbe avere terminazioni CR, CR + LF o solo LF. Voglio solo il supporto Ansi/Ascii, poiché attualmente sta usando RawByteString, tuttavia, puoi facilmente aggiungere il supporto UTF8 a questa classe.

  3. È necessaria una moderna classe Stream-like equivalente a TextFile (file di righe di testo). Lo chiamo TTextFile, ed è una classe lettore/scrittore che avvolge un Stream.

  4. Dovrebbe funzionare su base file 64 bit per file> 2 GB.

  5. Voglio che funzioni in Delphi 7, e anche in Delphi XE2, e tutto il resto.

  6. Volevo che fosse molto molto veloce.

-

di fare un WriteLn moderna su un flusso di file, si dovrebbe fare questo:

procedure TForm1.Button1Click(Sender: TObject); 
    var 
    ts:TTextStream; 
    begin 
    ts := TTextStream.Create('c:\temp\test.txt', fm_OpenWriteShared); 
    try 
    for t := 1 to 1000 do 
     ts.WriteLine('something'); 
    end; 
    finally 
     ts.Free; 
    end; 
    end; 

Ecco quello che si potrebbe scrivere se si desidera verificare lettura:

procedure TForm1.Button1Click(Sender: TObject); 
var 
ts:TTextStream; 
s:String; 
begin 
ts := TTextStream.Create('c:\temp\test.txt', fm_OpenReadShared); 
try 
while not ts.Eof do begin 
    s := ts.ReadLine; 
    doSomethingWith(s); 
end; 
finally 
    ts.Free; 
end; 
end; 

La classe è qui:

unit textStreamUnit; 
{$M+} 


{$R-} 

{ 
    textStreamUnit 

    This code is based on some of the content of the JvCsvDataSet written by Warren Postma, and others, 
    licensed under MOZILLA Public License. 
} 

interface 

uses 
    Windows, 
    Classes, 
    SysUtils; 


const 
    cQuote = #34; 
    cLf = #10; 
    cCR = #13; 

{ File stream mode flags used in TTextStream } 

    { Significant 16 bits are reserved for standard file stream mode bits. } 
    { Standard system values like fmOpenReadWrite are in SysUtils. } 
    fm_APPEND_FLAG = $20000; 
    fm_REWRITE_FLAG = $10000; 

    { combined Friendly mode flag values } 
    fm_Append   = fmOpenReadWrite or fm_APPEND_FLAG; 
    fm_OpenReadShared = fmOpenRead  or fmShareDenyWrite; 
    fm_OpenRewrite  = fmOpenReadWrite or fm_REWRITE_FLAG; 
    fm_Truncate  = fmCreate  or fm_REWRITE_FLAG; 
    fm_Rewrite   = fmCreate  or fm_REWRITE_FLAG; 

    TextStreamReadChunkSize = 8192; // 8k chunk reads. 

resourcestring 
    RsECannotReadFile = 'Cannot read file %'; 


type 
    ETextStreamException = class(Exception); 

{$ifndef UNICODE} 
    RawByteString=AnsiString; 
{$endif} 

    TTextStream = class(TObject) 
    private 
    FStream: TFileStream; // Tried TJclFileStream also but it was too slow! Do NOT use JCL streams here. -wpostma. 
    FFilename: string; 
    FStreamBuffer: PAnsiChar; 
    FStreamIndex: Integer; 
    FStreamSize: Integer; 
    FLastReadFlag: Boolean; 

    procedure _StreamReadBufInit; 
    public 
    function ReadLine: RawByteString; { read a string, one per line, wow. Text files. Cool eh?} 

    procedure Append; 
    procedure Rewrite; 

    procedure Write(const s: RawByteString);  {write a string. wow, eh? } 
    procedure WriteLine(const s: RawByteString); {write string followed by Cr+Lf } 

    procedure WriteChar(c: AnsiChar); 

    procedure WriteCrLf; 
    //procedure Write(const s: string); 

    function Eof: Boolean; {is at end of file? } 

    { MODE is typically a fm_xxx constant thatimplies a default set of stream mode bits plus some extended bit flags that are specific to this stream type.} 
    constructor Create(const FileName: string; Mode: DWORD = fm_OpenReadShared; Rights: Cardinal = 0); reintroduce; virtual; 
    destructor Destroy; override; 

    function Size: Int64; //override; // sanity 

    { read-only properties at runtime} 
    property Filename: string read FFilename; 
    property Stream: TFileStream read FStream; { Get at the underlying stream object} 
    end; 

implementation 





// 2 gigabyte file limit workaround: 
function GetFileSizeEx(h: HFILE; FileSize: PULargeInteger): BOOL; stdcall; external Kernel32; 

procedure TTextStream.Append; 
begin 
    Stream.Seek(0, soFromEnd); 
end; 

constructor TTextStream.Create(const FileName: string; Mode: DWORD; Rights: Cardinal); 
var 
    IsAppend: Boolean; 
    IsRewrite: Boolean; 
begin 
    inherited Create; 
    FFilename := FileName; 

    FLastReadFlag := False; 
    IsAppend := (Mode and fm_APPEND_FLAG) <> 0; 
    IsRewrite := (Mode and fm_REWRITE_FLAG) <> 0; 

    FStream := TFileStream.Create(Filename, {16 lower bits only}Word(Mode), Rights); 

    //Stream := FStream; { this makes everything in the base class actually work if we inherited from Easy Stream} 

    if IsAppend then 
    Self.Append // seek to the end. 
    else 
    Stream.Position := 0; 

    if IsRewrite then 
    Rewrite; 

    _StreamReadBufInit; 
end; 

destructor TTextStream.Destroy; 
begin 
    if Assigned(FStream) then 
    FStream.Position := 0; // avoid nukage 
    FreeAndNil(FStream); 
    FreeMem(FStreamBuffer); // Buffered reads for speed. 
    inherited Destroy; 
end; 

function TTextStream.Eof: Boolean; 
begin 
    if not Assigned(FStream) then 
    Result := False 
    //Result := True 
    else 
    Result := FLastReadFlag and (FStreamIndex >= FStreamSize); 
    //Result := FStream.Position >= FStream.Size; 
end; 

{ TTextStream.ReadLine: 
    This reads a line of text, normally terminated by carriage return and/or linefeed 
    but it is a bit special, and adapted for CSV usage because CR/LF characters 
    inside quotes are read as a single line. 

    This is a VERY PERFORMANCE CRITICAL function. We loop tightly inside here. 
    So there should be as few procedure-calls inside the repeat loop as possible. 


} 
function TTextStream.ReadLine: RawByteString; 
var 
    Buf: array of AnsiChar; 
    n: Integer; 
    QuoteFlag: Boolean; 
    LStreamBuffer: PAnsiChar; 
    LStreamSize: Integer; 
    LStreamIndex: Integer; 

    procedure FillStreamBuffer; 
    begin 
    FStreamSize := Stream.Read(LStreamBuffer[0], TextStreamReadChunkSize); 
    LStreamSize := FStreamSize; 
    if LStreamSize = 0 then 
    begin 
     if FStream.Position >= FStream.Size then 
     FLastReadFlag := True 
     else 
     raise ETextStreamException.CreateResFmt(@RsECannotReadFile, [FFilename]); 
    end 
    else 
    if LStreamSize < TextStreamReadChunkSize then 
     FLastReadFlag := True; 
    FStreamIndex := 0; 
    LStreamIndex := 0; 
    end; 

begin 
    { Ignore linefeeds, read until carriage return, strip carriage return, and return it } 
    SetLength(Buf, 150); 

    n := 0; 
    QuoteFlag := False; 

    LStreamBuffer := FStreamBuffer; 
    LStreamSize := FStreamSize; 
    LStreamIndex := FStreamIndex; 
    while True do 
    begin 
    if n >= Length(Buf) then 
     SetLength(Buf, n + 100); 

    if LStreamIndex >= LStreamSize then 
     FillStreamBuffer; 

    if LStreamIndex >= LStreamSize then 
     Break; 

    Buf[n] := LStreamBuffer[LStreamIndex]; 
    Inc(LStreamIndex); 

    case Buf[n] of 
     cQuote: {34} // quote 
     QuoteFlag := not QuoteFlag; 
     cLf: {10} // linefeed 
     if not QuoteFlag then 
      Break; 
     cCR: {13} // carriage return 
     begin 
      if not QuoteFlag then 
      begin 
      { If it is a CRLF we must skip the LF. Otherwise the next call to ReadLine 
       would return an empty line. } 
      if LStreamIndex >= LStreamSize then 
       FillStreamBuffer; 
      if LStreamBuffer[LStreamIndex] = cLf then 
       Inc(LStreamIndex); 

      Break; 
      end; 
     end 
    end; 
    Inc(n); 
    end; 
    FStreamIndex := LStreamIndex; 

    SetString(Result, PAnsiChar(@Buf[0]), n); 
end; 

procedure TTextStream.Rewrite; 
begin 
    if Assigned(FStream) then 
    FStream.Size := 0;// truncate! 
end; 

function TTextStream.Size: Int64; { Get file size } 
begin 
    if Assigned(FStream) then 
    GetFileSizeEx(FStream.Handle, PULargeInteger(@Result)) {int64 Result} 
    else 
    Result := 0; 
end; 

{ Look at this. A stream that can handle a string parameter. What will they think of next? } 
procedure TTextStream.Write(const s: RawByteString); 
begin 
    Stream.Write(s[1], Length(s)); {The author of TStreams would like you not to be able to just write Stream.Write(s). Weird. } 
end; 

procedure TTextStream.WriteChar(c: AnsiChar); 
begin 
    Stream.Write(c, SizeOf(AnsiChar)); 
end; 

procedure TTextStream.WriteCrLf; 
begin 
    WriteChar(#13); 
    WriteChar(#10); 
end; 

procedure TTextStream.WriteLine(const s: RawByteString); 
begin 
    Write(s); 
    WriteCrLf; 
end; 

procedure TTextStream._StreamReadBufInit; 
begin 
    if not Assigned(FStreamBuffer) then 
    begin 
    //FStreamBuffer := AllocMem(TextStreamReadChunkSize); 
    GetMem(FStreamBuffer, TextStreamReadChunkSize); 
    end; 
end; 

end. 
+0

Come funziona senza modifiche al codice aziendale?Quali sono stati esplicitamente esclusi? –

+0

Non riesco a fornire quella parte di questo approccio. Puoi scrivere una vera funzione di var-args in C/C++ ma non puoi con Pascal, e così, sei bloccato usando WriteLn, che ha il suo set di svantaggi. Ho postato questo perché altri utenti che cercano il nome della domanda potrebbero non essere contrari a modificare WriteLn (F, x, y, z) in F.WriteLine (FOrmat ('aaa', [x, y, z])) –

+0

Non vedo la verità dell'osservazione C. Per questo genere di cose sarebbe sufficiente la matrice di const, SE dovessi riscriverlo. Ma cambia il posizionamento esatto e la formattazione in virgola mobile che voglio evitare, dal momento che più client hanno i parser creati a mano (e probabilmente orribilmente bacati) per il formato. O alcuni clienti che notano un algo di arrotondamento leggermente diverso, ecc. –

1

Ho appena usato TextStreamUnit di Warren e funziona (grazie Warren), ma poiché anch'io avevo bisogno di un handle, ho modificato il codice sorgente per includerlo. Funzione IsFileInUse (FileName) utilizzato nel codice di esempio può essere trovato qui: http://delphi.about.com/od/delphitips2009/qt/is-file-in-use.htm. Questa combinazione mi ha aiutato a gestire tutte le situazioni testate quando più client leggono spesso alcuni file di rete, ma raramente ci scrivono, senza che alcune applicazioni serializzino richieste di scrittura. Sentiti libero di apportare eventuali miglioramenti al mio codice di esempio modificato. A proposito, probabilmente vorrai mostrare il cursore della clessidra durante questa operazione.

Ecco il codice di esempio:

procedure TForm1.Button1Click(Sender: TObject); 
const 
    MAX_RETRIES_TO_LOCK_FILE = 5; 
    TIME_BETWEEN_LOCK_RETRIES = 300; // ms 
    FILENAME = 'c:\temp\test.txt'; 
var 
    ts:TTextStream; 
    counter: byte; 
begin 
    try 
    for counter := 1 to MAX_RETRIES_TO_LOCK_FILE do 
    begin 
     if not IsFileInUse(FILENAME) then 
     begin 
     // ts := TTextStream.Create(FILENAME, fmCreate or fmShareDenyWrite); 
     ts := TTextStream.Create(FILENAME, fmOpenReadWrite or fmShareDenyWrite); 
     if ts.Handle > 0 then 
      Break 
     else 
      FreeAndNil(ts) 
     end 
     else 
     begin 
     Sleep(TIME_BETWEEN_LOCK_RETRIES); // little pause then try again 
     end; 
    end; 
    if ts.Handle > 0 then 
     ts.WriteLine('something') 
    else 
     MessageDlg('Failed to create create or access file, mtError, [mbOK], 0); 
    finally 
    if Assigned(ts) then 
    begin 
     FlushFileBuffers(ts.Handle); 
     FreeAndNil(ts); 
    end; 
    end; 
end; 

Qui è l'unità modificata:

unit TextStreamUnit; 
{$M+} 


{$R-} 

{ 
    TextStreamUnit 

    This code is based on some of the content of the JvCsvDataSet written by Warren Postma, and others, 
    licensed under MOZILLA Public License. 
} 

interface 

uses 
    Windows, 
    Classes, 
    SysUtils; 


const 
    cQuote = #34; 
    cLf = #10; 
    cCR = #13; 

{ File stream mode flags used in TTextStream } 

    { Significant 16 bits are reserved for standard file stream mode bits. } 
    { Standard system values like fmOpenReadWrite are in SysUtils. } 
    fm_APPEND_FLAG = $20000; 
    fm_REWRITE_FLAG = $10000; 

    { combined Friendly mode flag values } 
    fm_Append   = fmOpenReadWrite or fm_APPEND_FLAG; 
    fm_OpenReadShared = fmOpenRead  or fmShareDenyWrite; 
    fm_OpenRewrite  = fmOpenReadWrite or fm_REWRITE_FLAG; 
    fm_Truncate  = fmCreate  or fm_REWRITE_FLAG; 
    fm_Rewrite   = fmCreate  or fm_REWRITE_FLAG; 

    TextStreamReadChunkSize = 8192; // 8k chunk reads. 

resourcestring 
    RsECannotReadFile = 'Cannot read file %'; 


type 
    ETextStreamException = class(Exception); 

{$ifndef UNICODE} 
    RawByteString=AnsiString; 
{$endif} 

    TTextStream = class(TObject) 
    private 
    FStream: TFileStream; // Tried TJclFileStream also but it was too slow! Do NOT use JCL streams here. -wpostma. 
    FFilename: string; 
    FStreamBuffer: PAnsiChar; 
    FStreamIndex: Integer; 
    FStreamSize: Integer; 
    FLastReadFlag: Boolean; 
    FHandle: integer; 
    procedure _StreamReadBufInit; 
    public 
    function ReadLine: RawByteString; { read a string, one per line, wow. Text files. Cool eh?} 
    procedure Append; 
    procedure Rewrite; 
    procedure Write(const s: RawByteString);  {write a string. wow, eh? } 
    procedure WriteLine(const s: RawByteString); {write string followed by Cr+Lf } 
    procedure WriteChar(c: AnsiChar); 
    procedure WriteCrLf; 
    //procedure Write(const s: string); 
    function Eof: Boolean; {is at end of file? } 
    { MODE is typically a fm_xxx constant thatimplies a default set of stream mode bits plus some extended bit flags that are specific to this stream type.} 
    constructor Create(const FileName: string; Mode: DWORD = fm_OpenReadShared; Rights: Cardinal = 0); reintroduce; virtual; 
    destructor Destroy; override; 
    function Size: Int64; //override; // sanity 
    { read-only properties at runtime} 
    property Filename: string read FFilename; 
    property Handle: integer read FHandle; 
    property Stream: TFileStream read FStream; { Get at the underlying stream object} 
    end; 

implementation 


// 2 gigabyte file limit workaround: 
function GetFileSizeEx(h: HFILE; FileSize: PULargeInteger): BOOL; stdcall; external Kernel32; 

procedure TTextStream.Append; 
begin 
    Stream.Seek(0, soFromEnd); 
end; 

constructor TTextStream.Create(const FileName: string; Mode: DWORD; Rights: Cardinal); 
var 
    IsAppend: Boolean; 
    IsRewrite: Boolean; 
begin 
    inherited Create; 
    FFilename := FileName; 

    FLastReadFlag := False; 
    IsAppend := (Mode and fm_APPEND_FLAG) <> 0; 
    IsRewrite := (Mode and fm_REWRITE_FLAG) <> 0; 

    FStream := TFileStream.Create(Filename, {16 lower bits only}Word(Mode), Rights); 
    FHandle := FStream.Handle; 
    //Stream := FStream; { this makes everything in the base class actually work if we inherited from Easy Stream} 

    if IsAppend then 
    Self.Append // seek to the end. 
    else 
    Stream.Position := 0; 

    if IsRewrite then 
    Rewrite; 

    _StreamReadBufInit; 
end; 

destructor TTextStream.Destroy; 
begin 
    if Assigned(FStream) then 
    FStream.Position := 0; // avoid nukage 
    FreeAndNil(FStream); 
    FreeMem(FStreamBuffer); // Buffered reads for speed. 
    inherited Destroy; 
end; 

function TTextStream.Eof: Boolean; 
begin 
    if not Assigned(FStream) then 
    Result := False 
    //Result := True 
    else 
    Result := FLastReadFlag and (FStreamIndex >= FStreamSize); 
    //Result := FStream.Position >= FStream.Size; 
end; 

{ TTextStream.ReadLine: 
    This reads a line of text, normally terminated by carriage return and/or linefeed 
    but it is a bit special, and adapted for CSV usage because CR/LF characters 
    inside quotes are read as a single line. 

    This is a VERY PERFORMANCE CRITICAL function. We loop tightly inside here. 
    So there should be as few procedure-calls inside the repeat loop as possible. 
} 
function TTextStream.ReadLine: RawByteString; 
var 
    Buf: array of AnsiChar; 
    n: Integer; 
    QuoteFlag: Boolean; 
    LStreamBuffer: PAnsiChar; 
    LStreamSize: Integer; 
    LStreamIndex: Integer; 

    procedure FillStreamBuffer; 
    begin 
    FStreamSize := Stream.Read(LStreamBuffer[0], TextStreamReadChunkSize); 
    LStreamSize := FStreamSize; 
    if LStreamSize = 0 then 
    begin 
     if FStream.Position >= FStream.Size then 
     FLastReadFlag := True 
     else 
     raise ETextStreamException.CreateResFmt(@RsECannotReadFile, [FFilename]); 
    end 
    else 
    if LStreamSize < TextStreamReadChunkSize then 
     FLastReadFlag := True; 
    FStreamIndex := 0; 
    LStreamIndex := 0; 
    end; 

begin 
    { Ignore linefeeds, read until carriage return, strip carriage return, and return it } 
    SetLength(Buf, 150); 

    n := 0; 
    QuoteFlag := False; 

    LStreamBuffer := FStreamBuffer; 
    LStreamSize := FStreamSize; 
    LStreamIndex := FStreamIndex; 
    while True do 
    begin 
    if n >= Length(Buf) then 
     SetLength(Buf, n + 100); 

    if LStreamIndex >= LStreamSize then 
     FillStreamBuffer; 

    if LStreamIndex >= LStreamSize then 
     Break; 

    Buf[n] := LStreamBuffer[LStreamIndex]; 
    Inc(LStreamIndex); 

    case Buf[n] of 
     cQuote: {34} // quote 
     QuoteFlag := not QuoteFlag; 
     cLf: {10} // linefeed 
     if not QuoteFlag then 
      Break; 
     cCR: {13} // carriage return 
     begin 
      if not QuoteFlag then 
      begin 
      { If it is a CRLF we must skip the LF. Otherwise the next call to ReadLine 
       would return an empty line. } 
      if LStreamIndex >= LStreamSize then 
       FillStreamBuffer; 
      if LStreamBuffer[LStreamIndex] = cLf then 
       Inc(LStreamIndex); 
      Break; 
      end; 
     end 
    end; 
    Inc(n); 
    end; 
    FStreamIndex := LStreamIndex; 

    SetString(Result, PAnsiChar(@Buf[0]), n); 
end; 

procedure TTextStream.Rewrite; 
begin 
    if Assigned(FStream) then 
    FStream.Size := 0;// truncate! 
end; 

function TTextStream.Size: Int64; { Get file size } 
begin 
    if Assigned(FStream) then 
    GetFileSizeEx(FStream.Handle, PULargeInteger(@Result)) {int64 Result} 
    else 
    Result := 0; 
end; 

{ Look at this. A stream that can handle a string parameter. What will they think of next? } 
procedure TTextStream.Write(const s: RawByteString); 
begin 
    Stream.Write(s[1], Length(s)); {The author of TStreams would like you not to be able to just write Stream.Write(s). Weird. } 
end; 

procedure TTextStream.WriteChar(c: AnsiChar); 
begin 
    Stream.Write(c, SizeOf(AnsiChar)); 
end; 

procedure TTextStream.WriteCrLf; 
begin 
    WriteChar(#13); 
    WriteChar(#10); 
end; 

procedure TTextStream.WriteLine(const s: RawByteString); 
begin 
    Write(s); 
    WriteCrLf; 
end; 

procedure TTextStream._StreamReadBufInit; 
begin 
    if not Assigned(FStreamBuffer) then 
    begin 
    //FStreamBuffer := AllocMem(TextStreamReadChunkSize); 
    GetMem(FStreamBuffer, TextStreamReadChunkSize); 
    end; 
end; 

end.