Solved

Delete a Line from a File

Posted on 2004-08-25
11
258 Views
Last Modified: 2010-08-05
I have a file with alot of lines in it. For example: File.txt

60M200407014708990506058       0022D03533203534
60A200407014708990506058       CANC000000007890
60A200407014708990506058       I   000000021980
60A200407014708990506058       1800000000020850
60A200407014708990403225       CANC000000035836

I want to open this file "File.txt" and delete for example the first 2 lines of the file and the last one. then it woul stay for example:

60A200407014708990506058       I   000000021980
60A200407014708990506058       1800000000020850

with no blank spaces on the line deleted.
0
Comment
Question by:fabyola
  • 4
  • 2
  • 2
  • +3
11 Comments
 
LVL 17

Expert Comment

by:Wim ten Brink
ID: 11891534
You won't like the answer, I fear, but the easiest way is by using a stringlist...

var List:TStringList;
begin
  List := TStringList.Create;
  List.LoadFromFile('File.txt');
  // Delete last-to-first or else the index numbers get mised up.
  // Keep in mind, lists are 0-based...
  List.Delete(List.Count-1);
  List.Delete(1);
  List.Delete(0);
  List.SaveToFile('File.txt');
  List.Free;
end;

Other solutions that won't require you to read the whole file are a lot harder since you will actually have to move data around in the file. This means opening the file, finding the exact locations of the lines you want to remove and overwriting them with the data a bit further on... It would be possible to do and it would perform a bit faster, especially for huge files, but unfortunately it's not a simple solution...
0
 
LVL 4

Expert Comment

by:Magic55
ID: 11891587
create a TStringList and load the file with the funtion LoadFromFile.
Delete the lines and use SaveToFile
/ TK
0
 
LVL 17

Expert Comment

by:Wim ten Brink
ID: 11891594
Too late, Magik :P
0
Announcing the Most Valuable Experts of 2016

MVEs are more concerned with the satisfaction of those they help than with the considerable points they can earn. They are the types of people you feel privileged to call colleagues. Join us in honoring this amazing group of Experts.

 

Author Comment

by:fabyola
ID: 11892269
I need a way to do it withou loading it into a StringList because the file is Too big. I need a way to open the file and edit it.
0
 

Author Comment

by:fabyola
ID: 11892285
Or it doesn´t matter how big the file is ?
0
 
LVL 17

Expert Comment

by:Wim ten Brink
ID: 11892861
Doing it without loading the whole thing in memory is a bit more complicated. There's just no easy solution for this. One way to do this is like this:

var
  FileIn, FileOut: TextFile;
  Line: string;
  LineCount: Integer;
begin
  AssignFile(FileIn, 'File.txt');
  Reset(FileIn);
  AssignFile(FileIn, 'File.new.txt');
  Rewrite(FileOut);
  LineCount := 0;
  while not EOF(FileIn) do begin
    Inc(LineCount);
    ReadLn(FileIn, Line);
    if (LineCount <> 1) and (LineCount <> 2) and (LineCount <> 3009) then WriteLn(FileOut, Line);
  end;
  CloseFile(FileIn);
  CloseFile(FileOut);
end;

The drawback is of course that you now have two large files. You'll have to delete one and rename the other afterwards...

There's also an option by using only one file variable. Either as a file of byte or just as a plain file. (of nothing) But then you have to determine in your code where lines end and move data to the beginning, overwriting the parts that you want to be removed. Finally you'd have to truncate the file to it's new length. This is quite a bit complex code, though...
0
 
LVL 7

Expert Comment

by:LRHGuy
ID: 11892940
I did something similar...it's a copy procedure that takes a "filter" to select which lines to keep. It will work for any size file, as long as you have the disk space...

type
  tFilterFunc=function(aLine:string):boolean;

procedure CopyFileAndFilter(aFN_FROM,aFN_TO:string; aFilter:tFilterFunc);
var
  F,C:textfile;
  Line:String;
begin
  {$I-}
  assignfile(F,aFN_FROM);   {Get file}
  reset(F);
  assignfile(C,aFN_TO);
  rewrite(C);
  while not eof(F) do begin
    readln(F,Line);
    if aFilter(Line) then
      writeln(C,Line);
  end;
  closefile(C);
  closefile(F);
end;

function Filt(aLine:string):boolean; far;
begin
  Result:=pos('A',aline)=0;  //any kind of test you want, return RESULT=TRUE to keep the line.
end;

procedure DoItNow;
begin
  CopyFileAndFilter('Unit2.Pas','Unit2.txt',Filt);
 / / probably should erase the original file and rename new one here.
end;
0
 
LVL 14

Expert Comment

by:Pierre Cornelius
ID: 11893728
Procedure DeleteFirst2AndLastLineOfFile(FileName: string);
var OrigFile,NewFile: File;
    s: string;
begin
  AssignFile(NewFile, FileName);
  AssignFile(OrigFile, ChangeFileExt(FileName, '.old'));

  Reset(OrigFile);
  Rewrite(NewFile);

  //skip 1st two lines
  readln(OrigFiles);
  readln(OrigFiles);

  While Not Eof(OrigFile) do
  begin
    readln(OrigFile,s);
    if Not Eof(OrigFile) then writeln(NewFile,s); //only write it if not the last line
  end;
 
  CloseFile(OrigFile);
  CloseFile(NewFile);
  DeleteFile(ChangeFileExt(FileName, '.old')); //if you wish
end;

Kind regards
Pierre
0
 
LVL 14

Expert Comment

by:Pierre Cornelius
ID: 11893764
Sorry, LRHGuy. Didn't read all the comments before posting mine.
0
 
LVL 6

Expert Comment

by:bpana
ID: 11894141
>> I need a way to do it withou loading it into a StringList because the file is Too big. I need a way to open the file and edit it.
didn't quite understood how you want to edit it.

if manually then:

use a TMemo component. to load the file into the memo:
Memo1.Lines.LoadFromFile('YourFileName');

to save this back to the file:
Memo1.Lines.SaveToFile('YourFileName');
0
 
LVL 17

Accepted Solution

by:
Wim ten Brink earned 50 total points
ID: 11894764
Okay, a single-file method (that might ruin your textfile if it's not a proper textfile!)

procedure RemoveLine( Filename: string );
type
  TLine = record
    Start: Int64;
    Length: Int64;
    Delete: Boolean;
  end;
  TLines = array of TLine;
var
  AFile: file of Char;
  Lines: TLines;
  I, J: Integer;
  C: Char;
  CurPos: Int64;
begin
  SetLength( Lines, 0 );
  AssignFile( AFile, Filename );
  Reset( AFile );
  while not EOF( AFile ) do begin
    // We have a new line.
    I := Length( Lines );
    SetLength( Lines, I + 1 );
    // Set the start positions.
    Lines[ I ].Start := FilePos( AFile );
    // By default, delete none...
    Lines[ I ].Delete := False;
    // Find the end of the line.
    repeat
      Read( AFile, C );
    until EOF( AFile ) or ( C in [ #10, #13 ] );
    // Read the next character. #13 and #10 are generally in pairs.
    if not EOF( AFile ) then Read( AFile, C );
    Lines[ I ].Length := FilePos( AFile ) - Lines[ I ].Start;
  end;
  // Okay, deleting lines 1, 2, 101 and the last one... Don't forget the list is 0-based so subtract 1...
  Lines[ 0 ].Delete := True;
  Lines[ 1 ].Delete := True;
  Lines[ 100 ].Delete := True;
  Lines[ High( Lines ) ].Delete := True;
  // Now we're going to move all the bytes backwards... Rewind file.
  Seek( AFile, 0 );
  // Walk through the lines.
  for I := Low( Lines ) to High( Lines ) do begin
    // Move it if it isn't deleted.
    if not Lines[ I ].Delete then begin
      CurPos := FilePos( AFile );
      // But only move lines if they are further away in the file!
      if ( CurPos < Lines[ I ].Start ) then begin
        for J := 0 to Pred( Lines[ I ].Length ) do begin
          // Go to the character to copy.
          Seek( AFile, Lines[ I ].Start + J );
          Read( AFile, C );
          // Go to the position where the copy needs to be written.
          Seek( AFile, CurPos );
          Write( AFile, C );
          Inc( CurPos );
        end;
      end
      else begin
        Seek( AFile, Lines[ I ].Start + Lines[ I ].Length );
      end;
    end;
  end;
  // Cut off the rest of the file!
  Truncate( AFile );
  CloseFile( AFile );
end;

Well, you have to modify this code a bit to suit your own needs but it shows you how to remove lines without the need to read the whole file in memory. It could be made faster too by using blockreads and stuff like that but I'm just showing the principle here, not the optimized code.
I do several steps in this routine. First I determine where all the lines are located in the file. This information can then be used to select all the lines that I want to be removed. (The first two, one somewhere in-between and the one on the end.) Finally, I start moving the text in the file to the new positions. Once everything is processed, the file is truncated and task is done.

It could be a lot better optimized, though. And it could be done even without keeping a list of lines, but that would be a pretty complicated task. Besides, the list is useful if you want to delete lines counted from the end...

Keep in mind that this routine wants textfiles that end with a CR/LF on every line. And it will assume that if it finds one of them, the next one will be the other of this pair.
0

Featured Post

Announcing the Most Valuable Experts of 2016

MVEs are more concerned with the satisfaction of those they help than with the considerable points they can earn. They are the types of people you feel privileged to call colleagues. Join us in honoring this amazing group of Experts.

Question has a verified solution.

If you are experiencing a similar issue, please ask a related question

Suggested Solutions

Title # Comments Views Activity
Downloading email attachments 2 74
tvirtualstringtree freeze when load too manny images 10 63
DBGrid or StringGrid ? 6 93
DBCtrlGrid, Delphi, Scroll 8 15
Introduction The parallel port is a very commonly known port, it was widely used to connect a printer to the PC, if you look at the back of your computer, for those who don't have newer computers, there will be a port with 25 pins and a small print…
Introduction Raise your hands if you were as upset with FireMonkey as I was when I discovered that there was no TListview.  I use TListView in almost all of my applications I've written, and I was not going to compromise by resorting to TStringGrid…
Two types of users will appreciate AOMEI Backupper Pro: 1 - Those with PCIe drives (and haven't found cloning software that works on them). 2 - Those who want a fast clone of their boot drive (no re-boots needed) and it can clone your drive wh…
Finds all prime numbers in a range requested and places them in a public primes() array. I've demostrated a template size of 30 (2 * 3 * 5) but larger templates can be built such 210  (2 * 3 * 5 * 7) or 2310  (2 * 3 * 5 * 7 * 11). The larger templa…

825 members asked questions and received personalized solutions in the past 7 days.

Join the community of 500,000 technology professionals and ask your questions.

Join & Ask a Question