Remove lines from a file
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 cpp>#include <fstream>
- include <iostream>
- include <string>
- include <cstdlib>
- 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>
OCaml
<lang ocaml>let input_line_opt ic =
try Some (input_line ic) with End_of_file -> None
let delete_lines filename start skip =
if start < 1 || skip < 1 then invalid_arg "delete_lines"; let tmp_file = filename ^ ".tmp" in let ic = open_in filename and oc = open_out tmp_file in let until = start + skip - 1 in let rec aux i = match input_line_opt ic with | Some line -> if i < start || i > until then (output_string oc line; output_char oc '\n'); aux (succ i) | None -> close_in ic; close_out oc; Sys.remove filename; Sys.rename tmp_file filename in aux 1
let usage () =
Printf.printf "Usage:\n%s <filename> <startline> <skipnumber>\n" Sys.argv.(0); exit 0
let () =
if Array.length Sys.argv < 4 then usage (); delete_lines Sys.argv.(1) (int_of_string Sys.argv.(2)) (int_of_string Sys.argv.(3))</lang>
$ cal > cal.txt $ cat cal.txt juillet 2011 lu ma me je ve sa di 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 $ ocaml deleteLines.ml cal.txt 5 2 $ cat cal.txt juillet 2011 lu ma me je ve sa di 1 2 3 4 5 6 7 8 9 10 25 26 27 28 29 30 31
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>