Remove lines from a file
You are encouraged to solve this task according to the task description, using any language you may know.
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. An appropriate message should appear if an attempt is made to remove lines beyond the end of the file.
Ada
<lang Ada>with Ada.Text_IO, Ada.Directories, Ada.Command_Line, Ada.IO_Exceptions; use Ada.Text_IO;
procedure Remove_Lines_From_File is
Temporary: constant String := ".tmp";
begin
if Ada.Command_Line.Argument_Count /= 3 then raise Constraint_Error; end if; declare Filename: String := Ada.Command_Line.Argument(1); First: Positive := Integer'Value(Ada.Command_Line.Argument(2)); Last: Natural := Integer'Value(Ada.Command_Line.Argument(3)) + First - 1; Input, Output: File_Type; Line_Number: Positive := 1; begin Open(Input, In_File, Filename); -- open original file for reading Create(Output, Out_File, Filename & Temporary); -- write to temp. file while not End_Of_File(Input) loop declare Line: String := Get_Line(Input); begin if Line_Number < First or else Line_Number > Last then Put_Line(Output, Line); end if; end; Line_Number := Line_Number + 1; end loop; Close(Input); Close(Output); Ada.Directories.Rename(Old_Name => Filename & Temporary, New_Name => Filename); end;
exception
when Constraint_Error | Ada.IO_Exceptions.Name_Error => Put_Line("usage: " & Ada.Command_Line.Command_Name & " <filename> <first> <length>"); Put_Line(" opens <filename> for reading and " & "<filename>" & Temporary & " for temporary writing"); Put_Line(" requires first > 0, length >= 0");
end Remove_Lines_From_File;</lang>
AutoHotkey
<lang AHK>RemoveLines(filename, startingLine, numOfLines){
Loop, Read, %filename% if ( A_Index < StartingLine ) || ( A_Index >= StartingLine + numOfLines ) ret .= "`r`n" . A_LoopReadLine FileDelete, % FileName FileAppend, % SubStr(ret, 3), % FileName
}
SetWorkingDir, % A_ScriptDir RemoveLines("test.txt", 4, 3)</lang>
- Output
with test.txt starting as
1 2 3 4 5 6 7 8
Running the code it is now
1 2 3 7 8
AWK
<lang AWK>
- syntax: GAWK -f REMOVE_LINES_FROM_A_FILE.AWK
- show files after lines are removed:
- GAWK "FNR==1{print(FILENAME)};{print(FNR,$0)}" TEST1 TEST2 TEST3
BEGIN {
build_test_data() remove_lines("TEST0",1,1) remove_lines("TEST1",3,4) remove_lines("TEST2",9,3) remove_lines("TEST3",11,1) exit(errors+0)
} function build_test_data( fn,i,j) { # create 3 files with 10 lines each
for (i=1; i<=3; i++) { fn = "TEST" i for (j=1; j<=10; j++) { printf("line %d\n",j) >fn } close(fn) }
} function remove_lines(fn,start,number_of_lines, arr,fnr,i,n,rec,stop) {
stop = start + number_of_lines - 1 while (getline rec <fn > 0) { # read file fnr++ if (fnr < start || fnr > stop) { arr[++n] = rec } } close(fn) if (fnr == 0) { printf("error: file %s not found\n",fn) errors = 1 return } for (i=1; i<=n; i++) { # write file printf("%s\n",arr[i]) >fn } close(fn) if (stop > fnr) { printf("error: file %s trying to remove nonexistent lines\n",fn) errors = 1 }
} </lang>
C
<lang C>#include <stdio.h>
- include <stdlib.h> /* for atoi() and malloc() */
- include <string.h> /* for memmove() */
/* Conveniently print to standard error and exit nonzero. */
- define ERROR(fmt, arg) return fprintf(stderr, fmt "\n", arg)
int main(int argc, char **argv) {
FILE *fp; char *buf; size_t sz; int start, count, lines = 1; int dest = 0, src = 0, pos = -1;
/* Initialization and sanity checks */ if (argc != 4) ERROR("Usage: %s <file> <start> <count>", argv[0]);
if ((count = atoi(argv[3])) < 1) /* We're a no-op. */ return 0;
if ((start = atoi(argv[2])) < 1) ERROR("Error: <start> (%d) must be positive", start);
if ((fp = fopen(argv[1], "r")) == NULL) ERROR("No such file: %s", argv[1]);
/* Determine filesize and allocate a buffer accordingly. */ fseek(fp, 0, SEEK_END); sz = ftell(fp); buf = malloc(sz + 1); rewind(fp);
/* Fill the buffer, count lines, and remember a few important offsets. */ while ((buf[++pos] = fgetc(fp)) != EOF) { if (buf[pos] == '\n') { ++lines; if (lines == start) dest = pos + 1; if (lines == start + count) src = pos + 1; } }
/* We've been asked to do something weird; clean up and bail. */ if (start + count > lines) { free(buf); fclose(fp); ERROR("Error: invalid parameters for file with %d lines", --lines); }
/* Overwrite the lines to be deleted with the survivors. */ memmove(buf + dest, buf + src, pos - src);
/* Reopen the file and write back just enough of the buffer. */ freopen(argv[1], "w", fp); fwrite(buf, pos - src + dest, 1, fp);
free(buf); fclose(fp); return 0;
}</lang>
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>
Clojure
Simple solution dealing with most of the lines in memory. <lang clojure>(require '[clojure.java.io :as jio]
'[clojure.string :as str])
(defn remove-lines1 [filepath start nskip]
(let [lines (str/split-lines (slurp filepath)) new-lines (concat (take (dec start) lines) (drop (+ (dec start) nskip) lines)) diff (- (count lines) (count new-lines))] (when-not (zero? diff) (println "WARN: You are trying to remove lines beyond EOF")) (with-open [wrt (jio/writer (str filepath ".tmp"))] (.write wrt (str/join "\n" new-lines))) (.renameTo (jio/file (str filepath ".tmp")) (jio/file filepath))))</lang>
More complex solution for big file, one line at a time. <lang clojure>(require '[clojure.java.io :as jio]
'[clojure.string :as str])
(defn remove-lines2 [filepath start nskip]
(with-open [rdr (jio/reader filepath)] (with-open [wrt (jio/writer (str filepath ".tmp"))] (loop [s start n nskip] (if-let [line (.readLine rdr)] (cond (> s 1) (do (doto wrt (.write line) (.newLine)) (recur (dec s) n)) (pos? n) (recur s (dec n)) :else (do (doto wrt (.write line) (.newLine)) (recur s n))) (when (pos? n) (println "WARN: You are trying to remove lines beyond EOF")))))) (.renameTo (jio/file (str filepath ".tmp")) (jio/file filepath)))</lang>
Common Lisp
<lang lisp>(defun remove-lines (filename start num)
(let ((tmp-filename (concatenate 'string filename ".tmp"))
(lines-omitted 0))
;; Open a temp file to write the result to (with-open-file (out tmp-filename
:direction :output :if-exists :supersede :if-does-not-exist :create)
;; Open the original file for reading (with-open-file (in filename)
(loop for line = (read-line in nil 'eof) for i from 1 until (eql line 'eof) ;; Write the line to temp file if it is not in the omitted range do (if (or (< i start) (>= i (+ start num))) (write-line line out) (setf lines-omitted (1+ lines-omitted))))))
;; Swap in the temp file for the original (delete-file filename) (rename-file tmp-filename filename) ;; Warn if line removal went past the end of the file (when (< lines-omitted num) (warn "End of file reached with only ~d lines removed." lines-omitted))))</lang>
D
<lang d>import std.stdio, std.file, std.string;
void main() {
deleteLines("deleteline_test.txt", 1, 2);
}
void deleteLines(string name, int start, int num) in {
assert(start > 0, "Line counting must start at 1");
} body {
start--;
if (!exists(name) || !isFile(name)) throw new FileException("File not found");
auto lines = readText(name).splitLines(); if (lines.length < start + num) throw new Exception("Can't delete lines past the end of file!");
auto f = File(name, "w"); foreach (int i, line; lines) { if (start > i || i >= start + num) f.writeln(line); }
}</lang>
Erlang
<lang Erlang> -module( remove_lines ).
-export( [from_file/3, task/0] ).
from_file( Name, Start, How_many ) -> {Name, {ok, Binary}} = {Name, file:read_file( Name )}, Lines = compensate_for_last_newline( lists:reverse([X || X <- binary:split( Binary, <<"\n">>, [global] )]) ), {Message, Keep_lines} = keep_lines( Start - 1, How_many, Lines, erlang:length(Lines) - 1 ), ok = file:write_file( Name, [binary:list_to_bin([X, <<"\n">>]) || X <- Keep_lines] ), io:fwrite( "~s~n", [Message] ).
task() -> file:copy( "priv/foobar.txt", "foobar.txt" ), from_file( "foobar.txt", 1, 2 ).
compensate_for_last_newline( [<<>> | T] ) -> lists:reverse( T ); compensate_for_last_newline( Reversed_lines ) -> lists:reverse( Reversed_lines ).
keep_lines( Start, _How_many, Lines, Available ) when Start > Available -> {"Start > avaliable. Nothing removed.", Lines}; keep_lines( Start, How_many, Lines, Available ) when Start + How_many > Available -> Message = lists:flatten( io_lib:format("Start + How_many > avaliable. Only ~p removed.", [(Start + How_many) - Available]) ), {Keeps, _Removes} = lists:split( Start, Lines ), {Message, Keeps}; keep_lines( Start, How_many, Lines, _Available ) -> {Keeps, Removes} = lists:split( Start, Lines ), {"ok", Keeps ++ lists:nthtail( How_many, Removes )}. </lang>
- Output:
With "foobar.txt" that looks like this:
1 3 4 5 6 7 8 9 10
The resulting contents are:
3 4 5 6 7 8 9 10
ECL
Implemented for HPCC logical files, not single physical files, since all datasets in HPCC are distributed. <lang ECL> IMPORT STD; RemoveLines(logicalfile, startline, numlines) := FUNCTIONMACRO
EndLine := startline + numlines - 1; RecCnt := COUNT(logicalfile); Res := logicalfile[1..startline-1] + logicalfile[endline+1..]; RETURN WHEN(IF(RecCnt < EndLine,logicalfile,Res), IF(RecCnt < EndLine,STD.System.Log.addWorkunitWarning('Attempted removal past end of file-removal ignored')));
ENDMACRO; </lang> And a simple test case to run: <lang ECL> MyFile := DATASET(100,TRANSFORM({UNSIGNED1 RecID},SELF.RecID := COUNTER)); RemoveLines(MyFile,3,10);
</lang>
F#
<lang fsharp>open System open System.IO
let cutOut (arr : 'a[]) from n = // confine syntax highlighting confusion'
let slicer = fun i -> if i < from || (from + n) <= i then Some(arr.[i-1]) else None ((Array.choose slicer [| 1 .. arr.Length |]), from + n - arr.Length > 1)
[<EntryPoint>] let main argv =
let nums = Array.choose (System.Int32.TryParse >> function | true, v -> Some v | false, _ -> None) argv.[1..2] let lines = File.ReadAllLines(argv.[0]) let (sliced, tooShort) = cutOut lines nums.[0] nums.[1] if tooShort then Console.Error.WriteLine "Not enough lines" File.WriteAllLines(argv.[0], sliced) 0</lang>
Output <lang dos>D:\Projects\Rosetta>for /l %i in (1,1,5) do @echo %i >> foo
D:\Projects\Rosetta>Remove_lines_from_a_file.exe foo 1 2
D:\Projects\Rosetta>type foo 3 4 5
D:\Projects\Rosetta></lang>
Go
<lang go>package main
import (
"bytes" "errors" "fmt" "io/ioutil" "os"
)
func main() {
if err := removeLines("foobar.txt", 1, 2); err != nil { fmt.Println(err) }
}
func removeLines(fn string, start, n int) (err error) {
if start < 1 { return errors.New("invalid request. line numbers start at 1.") } if n < 0 { return errors.New("invalid request. negative number to remove.") } var f *os.File if f, err = os.OpenFile(fn, os.O_RDWR, 0); err != nil { return } defer func() { if cErr := f.Close(); err == nil { err = cErr } }() var b []byte if b, err = ioutil.ReadAll(f); err != nil { return } cut, ok := skip(b, start-1) if !ok { return fmt.Errorf("less than %d lines", start) } if n == 0 { return nil } tail, ok := skip(cut, n) if !ok { return fmt.Errorf("less than %d lines after line %d", n, start) } t := int64(len(b) - len(cut)) if err = f.Truncate(t); err != nil { return } if len(tail) > 0 { _, err = f.WriteAt(tail, t) } return
}
func skip(b []byte, n int) ([]byte, bool) {
for ; n > 0; n-- { if len(b) == 0 { return nil, false } x := bytes.IndexByte(b, '\n') if x < 0 { x = len(b) } else { x++ } b = b[x:] } return b, true
}</lang>
Haskell
<lang haskell>import System.Environment (getArgs)
main = getArgs >>= (\[a, b, c] ->
do contents <- fmap lines $ readFile a let b1 = read b :: Int c1 = read c :: Int putStr $ unlines $ concat [take (b1 - 1) contents, drop c1 $ drop b1 contents] )</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 fread file (;lines {~ <<< (start-1)+i.count) fwrite 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.
Java
<lang Java> import java.io.BufferedReader; import java.io.File; import java.io.FileReader; import java.io.FileWriter;
public class RemoveLines { public static void main(String[] args) { //Enter name of the file here String filename="foobar.txt"; //Enter starting line here int startline=1; //Enter number of lines here. int numlines=2;
RemoveLines now=new RemoveLines(); now.delete(filename,startline,numlines); } void delete(String filename, int startline, int numlines) { try { BufferedReader br=new BufferedReader(new FileReader(filename));
//String buffer to store contents of the file StringBuffer sb=new StringBuffer("");
//Keep track of the line number int linenumber=1; String line;
while((line=br.readLine())!=null) { //Store each valid line in the string buffer if(linenumber<startline||linenumber>=startline+numlines) sb.append(line+"\n"); linenumber++; } if(startline+numlines>linenumber) System.out.println("End of file reached."); br.close();
FileWriter fw=new FileWriter(new File(filename)); //Write entire string buffer into the file fw.write(sb.toString()); fw.close(); } catch (Exception e) { System.out.println("Something went horribly wrong: "+e.getMessage()); } } }
</lang>
Lasso
<lang Lasso>#!/usr/bin/lasso9
local( orgfilename = $argv -> second, file = file(#orgfilename), regexp = regexp(-find = `(?m)$`), content = #regexp -> split(-input = #file -> readstring) -> asarray, start = integer($argv -> get(3) || 1), range = integer($argv -> get(4) || 1) ) stdout(#content)
- file -> copyto(#orgfilename + '.org')
fail_if(#content -> size < (#start + #range), -1, 'Not that many rows in the file')
- content -> remove(#start, #range)
- file = file(#orgfilename)
- file -> opentruncate
- file -> dowithclose => {
#file -> writestring(#content -> join()) }</lang> Input:
1 Here is a row 2 Here is another row 3 This would be yet a row 4 me thinks the count is now four rows 5 Let's make this the last row
Call: <lang Lasso>./removelines textfile.txt 2 2</lang> Output:
1 Here is a row 4 me thinks the count is now four rows 5 Let's make this the last row
Liberty BASIC
It's always a bit dangerous to experiment with code that alters files. Un-rem the 'kill' to remove the temp file and the next line so the file is renamed to the original! <lang lb> call removeLines "foobar.txt", 1, 2 end
sub removeLines filename$, start, count
open filename$ for input as #in open filename$ + ".tmp" for output as #out lineCounter = 1 firstAfterIgnored = start + count while not(eof(#in)) line input #in, s$ if lineCounter < start or lineCounter >= firstAfterIgnored then print #out, s$ end if lineCounter = lineCounter + 1 wend close #in close #out 'kill filename$ 'name filename$ + ".tmp" as filename$
end sub </lang>
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
Pascal
This solution is a bit complicated but the direct use of a TStringList (LoadFromFile, SaveToFile) is not correc ; in fact, it appends CR/LF sometimes, same remark for ReadLn, WriteLn. TFileStream is better. Temp file avoid memory comsumption.
<lang Pascal>program RemLines;
{$mode objfpc}{$H+}
uses
{$IFDEF UNIX}{$IFDEF UseCThreads} cthreads, {$ENDIF}{$ENDIF} Classes, SysUtils;
type
TRLResponse=(rlrOk, rlrEmptyFile, rlrNotEnoughLines);
function RemoveLines(const FileName: String; const From, Count: Integer): TRLResponse; const
LineOffs = Length(LineEnding);
var
TIn, TOut: TFileStream; tmpFn, MemBuff, FileBuff: String; EndingPos, CharRead, LineNumber: Integer;
procedure WriteLine(Line: String); begin if ((From > 1) and (LineNumber = 1)) or ((From = 1) and (LineNumber = (From+Count))) then // First line to write, without LineEnding => Line unchanged else if ((From = 1) or (From <= LineNumber)) and (LineNumber < (From+Count)) then // No line to write Line := else // all other cases, write Line preceded (!) by LineEnding Line := LineEnding + Line; // Write if Line <> then TOut.Write(Line[1], Length(Line)); End;
begin
if not FileExists(FileName) then raise Exception.CreateFmt('No such file %s', [FileName]); if From < 1 then raise Exception.Create('First line must be >= 1');
tmpFn := GetTempFileName(ExtractFilePath(FileName), );
TIn := TFileStream.Create(FileName, fmOpenRead); try TOut := TFileStream.Create(tmpFn, fmCreate); try FileBuff := StringOfChar(' ', 1024); // Reserve memory in a string LineNumber := 0; MemBuff := ; while True do begin CharRead := TIn.Read(FileBuff[1], 1024); if (CharRead = 0) then break; // no more char to process MemBuff += Copy(FileBuff, 1, CharRead); // op += is FPC specific while True do begin // LineEnding can contain 1 or 2 chars, depending on the OS EndingPos := Pos(LineEnding, MemBuff); if EndingPos = 0 then break; // EndingLine in the next reading, maybe Inc(LineNumber); WriteLine(Copy(MemBuff, 1, EndingPos - 1)); MemBuff := Copy(MemBuff, EndingPos + LineOffs, MaxInt); // Loop for another line in MemBuff end; end; Inc(LineNumber); WriteLine(MemBuff); // Writes what remains finally TOut.Free; end; finally TIn.Free; end; // Temp File replaces the original file. if DeleteFile(FileName) then RenameFile(tmpFn, FileName) else raise Exception.Create('Unable to process the file'); // Response if (LineNumber = 0) then Result := rlrEmptyFile else if (LineNumber < (From+Count-1)) then Result := rlrNotEnoughLines else Result := rlrOk;
End;
var
FileName: String;
begin
FileName := IncludeTrailingPathDelimiter(ExtractFilePath(ParamStr(0))) + 'test.txt'; try case RemoveLines(FileName, 4, 3) of rlrOk: WriteLn('Lines deleted'); rlrEmptyFile: WriteLn(Format('File "%s" is empty!', [FileName])); rlrNotEnoughLines: WriteLn('Cant delete lines past the end of file'); end except on E: Exception do WriteLn('Error: ' + E.Message); end; ReadLn;
End. </lang>
- Output:
Perl
The value of deleting certain lines from a file notwithstanding, here's how you'd normally do it in Perl (call with perl rmlines -from=3 -to=10 filename
:<lang perl>#!/usr/bin/perl -n -s -i
print unless $. >= $from && $. <= $to;</lang>
If you want a backup file (maybe because deleting lines from a file in place is a pretty silly idea), change the -i
in the first line to -i.bak
, for example.
<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>
Perl 6
<lang perl6>sub MAIN ($filename, $beg, $len) {
my @lines = split /^^/, slurp $filename; unlink $filename; # or rename splice(@lines,$beg,$len) == $len or warn "Too few lines"; spurt $filename, @lines;
}</lang>
- Output:
$ cal >foo $ ./rmlines Usage: rmlines <filename> <beg> <len> $ ./rmlines foo 1 2 $ cat foo 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
PicoLisp
<lang PicoLisp>(de deleteLines (File Start Cnt)
(let L (in File (make (until (eof) (link (line))))) (if (> (+ (dec 'Start) Cnt) (length L)) (quit "Not enough lines") (out File (mapc prinl (cut Start 'L)) (mapc prinl (nth L (inc Cnt))) ) ) ) )</lang>
PowerBASIC
<lang powerbasic>#DIM ALL
FUNCTION PBMAIN () AS LONG
DIM filespec AS STRING, linein AS STRING DIM linecount AS LONG, L0 AS LONG, ok AS LONG
filespec = DIR$(COMMAND$(1)) WHILE LEN(filespec) linecount = 0: ok = 0 OPEN filespec FOR INPUT AS 1 OPEN filespec & ".tmp" FOR OUTPUT AS 2 DO UNTIL EOF(1) LINE INPUT #1, linein INCR linecount IF VAL(COMMAND$(2)) <> linecount THEN PRINT #2, linein ELSE ok = -1 FOR L0 = 2 TO VAL(COMMAND$(3)) IF EOF(1) THEN ok = 1 PRINT "Tried to remove beyond the end of "; filespec EXIT DO END IF LINE INPUT #1, linein NEXT END IF LOOP IF 0 = ok THEN PRINT "Lines to remove are beyond the end of "; filespec ELSEIF -1 = ok THEN PRINT filespec; ": done" END IF CLOSE filespec = DIR$ WEND
END FUNCTION</lang>
Sample output:
E:\users\Erik\Desktop>rmvlns.exe ?.txt 500 100 Lines to remove are beyond the end of 0.txt Tried to remove beyond the end of 1.txt 2.txt: done 3.txt: done 4.txt: done
Python
Uses the fileinput standard module. <lang python>#!/usr/bin/env python
import fileinput, sys
fname, start, count = sys.argv[1:4] start, count = int(start), int(count)
for line in fileinput.input(fname, inplace=1, backup='.orig'):
if start <= fileinput.lineno() < start + count: pass else: print line[:-1]
fileinput.close()</lang>
- Output:
There follows a Linux shell session showing the programs use to remove four lines starting from the second, of a file that starts with the digits one to ten on separate lines. The program keeps the original file with a ,orig prefix.
paddy@paddy-ThinkPad-T61:~$ seq 1 10 > testfile.txt paddy@paddy-ThinkPad-T61:~$ ./remove_lines.py testfile.txt 2 4 paddy@paddy-ThinkPad-T61:~$ cat testfile.txt 1 6 7 8 9 10 paddy@paddy-ThinkPad-T61:~$ cat testfile.txt.orig 1 2 3 4 5 6 7 8 9 10 paddy@paddy-ThinkPad-T61:~$
Racket
<lang Racket>
- lang racket
(define (remove-lines file from num)
(define lines (file->lines file)) (define-values [pfx rest] (split-at lines (sub1 from))) (display-lines-to-file (append pfx (drop rest num)) file #:exists 'replace))
</lang>
REXX
This example is operating system dependent as this program uses the ERASE and RENAME commands. <lang rexx>/*REXX program to read a specified file and delete specified record(s). */ parse arg iFID ',' at ',' many /*input FID, del start, how many.*/ if iFID= then call er "no input fileID specified." if at= then call er "no start number specified." if many= then many=1 /*Not specified? Assume 1 line.*/ stop=at+many-1 /*calculate high end of deletes. */ oFID=iFID'.out' /*the name of the output file. */ w=0
do j=1 while lines(iFID)\==0 /*J is the line number being read*/ x=linein(iFID) /*read a line from the input file*/ if j>=at & j<=stop then iterate /*if in the range, then ignore it*/ call lineout oFID,x; w=w+1 /*write line, bump the write cnt.*/ end
j=j-1 if j<stop then say "number of lines in file is less than the range given." q='"' /*handle cases of blanks in FID. */ 'ERASE' q || ifid || q 'RENAME' q || ofid || q q || ifid || q say 'file ' iFID " had" j 'record's(j)", it now has" w 'record's(w)"." exit /*stick a fork in it, we're done.*/ /*──────────────────────────────────ER subroutine───────────────────────*/ er: say; say '***error!***; say; say arg(1); say; exit 13 /*──────────────────────────────────S subroutine────────────────────────*/ s: if arg(1)==1 then return arg(3);return word(arg(2) 's',1) /*plurals.*/</lang> output when using the input of: foobar.txt,2,4
file foobar.txt had 10 records, it now has 6 records.
file before processing
1 2 3 4 5 6 7 8 9 10
file after processing
1 6 7 8 9 10
Ruby
<lang ruby>require 'tempfile'
def remove_lines(filename, start, num)
tmp = Tempfile.open(filename) do |fp| File.foreach(filename) do |line| if $. >= start and num > 0 num -= 1 else fp.puts line end end fp end puts "Warning: End of file encountered before all lines removed" if num > 0 FileUtils.copy(tmp.path, filename) tmp.unlink
end
- Test code
def setup(filename, start, remove)
puts "remove #{remove} lines starting at line #{start}" File.open(filename, "w") {|fh| (1..5).each {|i| fh.puts " "*i + i.to_s}} puts "before:", File.read(filename)
end
def teardown(filename)
puts "after:", File.read(filename) puts File.unlink(filename)
end
filename = "foobar.txt" start = 2 [2, 6].each do |remove|
setup(filename, start, remove) remove_lines(filename, start, remove) teardown(filename)
end</lang>
- Output:
remove 2 lines starting at line 2 before: 1 2 3 4 5 after: 1 4 5 remove 6 lines starting at line 2 before: 1 2 3 4 5 Warning: End of file encountered before all lines removed after: 1
Run BASIC
<lang runbasic>fileName$ = "aFile.txt" startLine = 100 lineCount = 10
open filename$ for input as #in open filename$ ; "_tmp" for output as #out
while not(eof(#in))
lineNum = lineNum + 1 line input #in, a$ if lineNum < startLine or lineNum >= startLine + lineCount then print #out, a$
wend close #in close #out</lang>
Seed7
<lang seed7>$ include "seed7_05.s7i";
include "osfiles.s7i";
const proc: removeLines (in string: fileName, in integer: start, in integer: count) is func
local var file: inFile is STD_NULL; var file: outFile is STD_NULL; var integer: lineNumber is 1; var string: line is ""; begin inFile := open(fileName, "r"); outFile := open(fileName & ".tmp", "w"); while hasNext(inFile) do line := getln(inFile); if lineNumber < start or lineNumber >= start + count then writeln(outFile, line); end if; incr(lineNumber); end while; close(inFile); close(outFile); removeFile(fileName); moveFile(fileName & ".tmp", fileName); end func;
const proc: main is func
begin if length(argv(PROGRAM)) = 3 then removeLines(argv(PROGRAM)[1], integer parse (argv(PROGRAM)[2]), integer parse (argv(PROGRAM)[3])); end if; end func;</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>
TUSCRIPT
<lang tuscript> $$! input=testfile,begnr=3,endnr=4 $$ MODE TUSCRIPT - CREATE inputfile ERROR/STOP CREATE (input,FDF-o,-std-) FILE/ERASE $input
LOOP n=1,9 content=CONCAT ("line",n) DATA {content} ENDLOOP
ENDFILE
- CREATE outputfile output="outputfile"
ERROR/STOP CREATE (output,fdf-o,-std-) ACCESS q: READ/RECORDS/utf8 $input L,line ACCESS z: WRITE/RECORDS/utf8 $output L,line PRINT "content: of output-file" LOOP/9999 READ/NEXT/EXIT q IF (begnr<=L&&endnr>=L) CYCLE PRINT line WRITE z ENDLOOP ENDACCESS/PRINT q ENDACCESS/PRINT z </lang> Output:
Start MAKRO auf: XXXXXX am: 02.12.11 um: 17:31:50 content: of output-file line1 line2 line5 line6 line7 line8 line9 **** Eingabe: 9 Sätze von Datei -*TESTFILE Satzlänge: 5 **** Ausgabe: 7 Sätze auf Datei -*OUTPUTFILE Satzlänge: 5 Ende MAKRO auf: XXXXXX am: 02.12.11 um: 17:31:50
UNIX Shell
<lang bash>
- !/bin/sh
error() {
echo "$*" exit 1
}
[ $# -ne 3 ] && error "Incorrect number of parameters"
file=$1 start=$2 end=$3
[ -f $file ] || error "$file does not exist"
sed $start,${end}d $file >/tmp/$$ && mv /tmp/$$ $file </lang>
If you have GNU sed on the system, you can replace the last line with
<lang bash> sed -i $start,${end}d $file </lang>
zkl
Two solutions, one in memory and the other read the file line by line and hope the OS buffers. Write to stdout to avoid "what to use as dst" questions. <lang zkl>fcn deleteLinesM(fname, start,num){
blob:=File(fname).read(); // file to memory n:=blob.seek(Void,start-1); // seek to line and remember it blob.del(n,blob.seek(Void,num)-n); File.stdout.write(blob);
} deleteLinesM("nn.zkl", 2,5);</lang>
- Output:
fcn deleteLinesM(fname, start,num){ } deleteLinesM("nn.zkl", 2,5);
Line by line: <lang zkl>fcn deleteLinesL(fname, start,num){
if (start < 1) throw(Exception.ValueError); f:=File(fname); do(start-1) { File.stdout.write(f.readln()) } do(num) { f.readln(); } f.pump(File.stdout.write);
} deleteLinesL("nn.zkl", 2,5);</lang>
- Output:
fcn deleteLinesL(fname, start,num){ } deleteLinesL("nn.zkl", 2,5);