Remove lines from a file

From Rosetta Code
Revision as of 00:37, 31 July 2011 by Ulrie (talk | contribs)
Remove lines from a file is a draft programming task. It is not yet considered ready to be promoted as a complete task, for reasons that should be found in its talk page.

The task is to demonstrate how to remove a specific line or a number of lines from a file. This should be implemented as a routine that takes three parameters (filename, starting line, and the number of lines to be removed). For the purpose of this task, line numbers and the number of lines start at one, so to remove the first two lines from the file foobar.txt, the parameters should be: foobar.txt, 1, 2

Empty lines are considered and should still be counted, and if the specified line is empty, it should still be removed. However, an appropriate message should appear, if an attempt is made to remove lines beyond the end of the file.

C++

<lang C++>#include <fstream>

  1. include <iostream>
  2. include <string>
  3. include <cstdlib>
  4. include <list>

void deleteLines( const std::string & , int , int ) ;

int main( int argc, char * argv[ ] ) {

  if ( argc != 4 ) {
     std::cerr << "Error! Invoke with <deletelines filename startline skipnumber>!\n" ;
     return 1 ;
  }
  std::string filename( argv[ 1 ] ) ;
  int startfrom = atoi( argv[ 2 ] ) ;
  int howmany = atoi( argv[ 3 ] ) ;
  deleteLines ( filename , startfrom , howmany ) ;
  return 0 ;

}

void deleteLines( const std::string & filename , int start , int skip ) {

  std::ifstream infile( filename.c_str( ) , std::ios::in ) ;
  if ( infile.is_open( ) ) {
     std::string line ;
     std::list<std::string> filelines ;
     while ( infile ) { 

getline( infile , line ) ; filelines.push_back( line ) ;

     }
     infile.close( ) ;
     if ( start > filelines.size( ) ) {

std::cerr << "Error! Starting to delete lines past the end of the file!\n" ; return ;

     }
     if ( ( start + skip ) > filelines.size( ) ) {

std::cerr << "Error! Trying to delete lines past the end of the file!\n" ; return ;

     }
     std::list<std::string>::iterator deletebegin = filelines.begin( ) , deleteend ;
     for ( int i = 1 ; i < start ; i++ )

deletebegin++ ;

     deleteend = deletebegin ;
     for( int i = 0 ; i < skip ; i++ ) 

deleteend++ ;

     filelines.erase( deletebegin , deleteend ) ;
     std::ofstream outfile( filename.c_str( ) , std::ios::out | std::ios::trunc ) ;
     if ( outfile.is_open( ) ) {

for ( std::list<std::string>::const_iterator sli = filelines.begin( ) ; sli != filelines.end( ) ; sli++ ) outfile << *sli << "\n" ;

     }
     outfile.close( ) ;
  }
  else {
     std::cerr << "Error! Could not find file " << filename << " !\n" ;
     return ;
  }

}</lang>

Icon and Unicon

<lang Icon>procedure main() # remove lines

  removelines("foo.bar",3,2) | stop("Failed to remove lines.")

end

procedure removelines(fn,start,skip)

  f := open(fn,"r") | fail                  # open to read
  every put(F := [],|read(f))               # file to list
  close(f)
  if *F < start-1+skip then fail
  every F[start - 1 + (1 to skip)] := &null # mark delete
  
  f := open(fn,"w") | fail                  # open to rewrite
  every write(\!F)                          # write non-nulls
  close(f)
  return                                    # done

end</lang>

J

<lang j>removeLines=:4 :0

 'count start'=. x
 file=. boxxopen y
 lines=. <;.2]1!:1 file
 (;lines {~ <<< (start-1)+i.count) 1!:2 file

)</lang>

Thus: <lang bash>$ cal >cal.txt $ cat cal.txt

     July 2011

Su Mo Tu We Th Fr Sa

               1  2
3  4  5  6  7  8  9

10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31</lang>

<lang j> 2 1 removeLines 'cal.txt'</lang>

<lang bash>$ cat cal.txt

               1  2
3  4  5  6  7  8  9

10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31</lang>

Note that this code assumes that the last character in the file is the line end character.

Lua

<lang lua>function remove( filename, starting_line, num_lines )

   local fp = io.open( filename, "r" )
   if fp == nil then return nil end
   content = {}
   i = 1;
   for line in fp:lines() do
       if i < starting_line or i >= starting_line + num_lines then

content[#content+1] = line end i = i + 1

   end
   if i > starting_line and i < starting_line + num_lines then

print( "Warning: Tried to remove lines after EOF." )

   end
   fp:close()
   fp = io.open( filename, "w+" )
   for i = 1, #content do

fp:write( string.format( "%s\n", content[i] ) )

   end
   fp:close()

end</lang>

Perl

<lang Perl>#!/usr/bin/perl -w use strict ; use Tie::File ;

sub deletelines {

  my $arguments = shift ;
  my( $file , $startfrom , $howmany ) = @{$arguments} ;
  tie my @lines , 'Tie::File' , $file or die "Can't find file $file!\n" ;
  my $nrecs = @lines ;
  if ( $startfrom > $nrecs ) {
     print "Error! Starting to delete lines past the end of the file!\n" ;
     return ;
  }
  if ( ( $startfrom + $howmany ) > $nrecs ) {
     print "Error! Trying to delete lines past the end of the file!\n" ;
     return ;
  }
  splice @lines , $startfrom - 1 , $howmany ;
  untie @lines ;

}

if ( @ARGV != 3 ) {

  print "Error! Invoke with deletelines <filename> <start> <skiplines> !\n" ;
  exit( 1 ) ;

}

deletelines( \@ARGV ) ;</lang>


Tcl

<lang tcl>proc removeLines {fileName startLine count} {

   # Work out range to remove
   set from [expr {$startLine - 1}]
   set to [expr {$startLine + $count - 2}]
   # Read the lines
   set f [open $fileName]
   set lines [split [read $f] "\n"]
   close $f
   # Write the lines back out, without removed range
   set f [open $fileName w]
   puts -nonewline $f [join [lreplace $lines $from $to] "\n"]
   close $f

}</lang>