| 
      ...read a string from a text file at a certain line number?
     | 
   
   
    | Autor: 
      Barry Kelly     | 
   
  | [ Print tip 
] |   |   |   
 
 
 
{ 
 Abstract: 
  Im trying to write a function that, given a FileName and a line number 
  returns the entire line in a string. 
} 
 
{ 
 The following technique is useful for high-speed processing. 
 The sample program file, save it with a .pas or .dpr filename and compile it. 
} 
 
 
{$APPTYPE CONSOLE} 
uses SysUtils, Classes; 
 
function GrabLine(const AFileName: string; ALine: Integer): string; 
var 
  fs: TFileStream; 
  buf: packed array[0..4095] of Char; 
  bufRead: Integer; 
  bufPos: PChar; 
  lineStart: PChar; 
  tmp: string; 
begin 
  fs := TFileStream.Create(AFileName, fmOpenRead); 
  try 
    Dec(ALine); 
    bufRead := 0; 
    bufPos := nil; 
 
    { read the first line specially } 
    if ALine = 0 then 
    begin 
      bufRead := fs.Read(buf, SizeOf(buf)); 
      if bufRead = 0 then 
        raise Exception.Create('Line not found'); 
      bufPos := buf; 
    end else 
      while ALine > 0 do 
      begin 
        { read in a buffer } 
        bufRead := fs.Read(buf, SizeOf(buf)); 
        if bufRead = 0 then 
          raise Exception.Create('Line not found'); 
        bufPos := buf; 
        while (bufRead > 0) and (ALine > 0) do 
        begin 
          if bufPos^ = #10 then 
            Dec(ALine); 
          Inc(bufPos); 
          Dec(bufRead); 
        end; 
      end; 
    { Found the beginning of the line at bufPos... scan for end. 
      2 cases: 
        1) we'll find it before the end of this buffer 
        2) it'll go beyond this buffer and into n more buffers } 
    lineStart := bufPos; 
    while (bufRead > 0) and (bufPos^ <> #10) do 
    begin 
      Inc(bufPos); 
      Dec(bufRead); 
    end; 
    { if bufRead is positive, we'll have found the end and we can leave. } 
    SetString(Result, lineStart, bufPos - lineStart); 
    { determine if there are more buffers to process } 
    while bufRead = 0 do 
    begin 
      bufRead := fs.Read(buf, SizeOf(buf)); 
      lineStart := buf; 
      bufPos := buf; 
      while (bufRead > 0) and (bufPos^ <> #10) do 
      begin 
        Inc(bufPos); 
        Dec(bufRead); 
      end; 
      SetString(tmp, lineStart, bufPos - lineStart); 
      Result := Result + tmp; 
    end; 
  finally 
    fs.Free; 
  end; 
end; 
 
function GrabLine2(const s: string; ALine: Integer): string; 
var 
  sl: TStringList; 
begin 
  sl := TStringList.Create; 
  try 
    sl.LoadFromFile(s); 
    Result := sl[ALine - 1]; // index off by one 
  finally 
    sl.Free; 
  end; 
end; 
 
begin 
  Writeln(GrabLine(ParamStr(1), StrToInt(ParamStr(2)))); 
  Writeln(GrabLine2(ParamStr(1), StrToInt(ParamStr(2)))); 
end. 
 
{ 
 
Call it like 'getline testfile.txt 20000', depending on what you call the 
.pas (or .dpr) file. For large (i.e. tens of megabytes) files, the (rather 
complex) scanning function easily beats the memory expensive StringList 
version. 
 
-- Barry 
} 
 
 
 
  
                       |