Read a specific line from a file

From Rosetta Code
Read a specific line from a file
You are encouraged to solve this task according to the task description, using any language you may know.

Some languages have special semantics for obtaining a known line number from a file. The task is to demonstrate how to obtain the contents of a specific line within a file. For the purpose of this task demonstrate how to the contents of the seventh line of a file can be obtained, and store this in a variable or in memory (for potential future use within the program if the code were to become embedded). If the file does not contain seven lines, or the seventh line is empty, or too big to be retrieved, output an appropriate message. If no special semantics are available for obtaining the required line, it is permissible to read line by line. Note that empty lines are considered and should still be counted. Note that for functional languages or languages without variables or storage, it is permissible to output the extracted data to standard output.


A github repository was created for this solution

The repository contains test cases for specific file types the program might be run against.

Based on the task description the following requirements were identified

  • Display a specific message for a file having fewer than 7 lines
  • Display a specific message if the 7th line is empty
  • Display a specific message if the 7th line is too big to be retrieved and stored in memory
  • Acquire the 7th line of a file, using built-in language features/standard libraries if accessible
  • Store the retrieved line in a variable/memory location

The following undefined behavior was identified and addressed

  • The task description does not define an empty line. The program assumes that an empty line contains only a line break character. A line containing whitespace (' ', '\t') is not considered empty.
  • Things considered an error in execution are printed out to stderr
  • In case of an encountered error the return code of the program is changed to Failure (1)

In order to ease testing - the program expects the file name to be provided as the first argument on the command line.

Additional checks are performed by the program

  • Tests if the file name was provided on the command line
  • Tests if the provided file exists
  • Tests if the file is an ordinary file (not a directory, symbolic link etc)

<lang ada>with Ada.Text_IO; with Ada.Directories; with Ada.IO_Exceptions; with Ada.Command_Line;

procedure Rosetta_Read is

  File : Ada.Text_IO.File_Type;


  if Ada.Command_Line.Argument_Count = 0 then
     Ada.Text_IO.Put_Line (File => Ada.Text_IO.Standard_Error,
                           Item => "Usage: " &
                             Ada.Command_Line.Command_Name &
                             " file_name");
     Ada.Command_Line.Set_Exit_Status (Ada.Command_Line.Failure);
  end if;
  -- Make sure the target exists and is a file (could be a directory etc.)
     use Ada.Command_Line; -- Argument, Set_Exit_Status, Failure
     use Ada.Directories; -- Exists, Kind
     if not Exists( Argument (Number => 1))
       or else not (Kind (Argument (Number => 1)) = Ordinary_File) then
        Ada.Text_IO.Put_Line (File => Ada.Text_IO.Standard_Error,
                              Item => "File " &
                                Argument (Number => 1) &
                                " does not exist or is not a regular file");
        Set_Exit_Status (Failure);
  end if;
  Ada.Text_IO.Open (File,
                    Mode => Ada.Text_IO.In_File,
                    Name => Ada.Command_Line.Argument (Number => 1));
  Ada.Text_IO.Set_Line (File, 7);
        Stored_Line : constant String := Ada.Text_IO.Get_Line (File);
        -- We assume that lines containing
        -- only whitespace are *not* empty.
        if Stored_Line = "" then
           Ada.Text_IO.Put_Line ("Line 7 in " &
                                   Ada.Command_Line.Argument (Number => 1) &
                                   " is empty");
           Ada.Text_IO.Put_Line (Stored_Line);
        end if;
     when Standard.Storage_Error =>
        Ada.Text_IO.Put_Line (File => Ada.Text_IO.Standard_Error,
                              Item => "Line 7 of " &
                                Ada.Command_Line.Argument (Number => 1) &
                                " too long to store in memory available to this program");
        if Ada.Text_IO.Is_Open (File) then
           Ada.Text_IO.Close (File);
        end if;
        Ada.Command_Line.Set_Exit_Status (Ada.Command_Line.Failure);
  Ada.Text_IO.Close (File);


  when Ada.IO_Exceptions.End_Error =>
     Ada.Text_IO.Put_Line (File => Ada.Text_IO.Standard_Error,
                           Item => Ada.Command_Line.Argument (Number => 1) &
                             " has fewer than 7 lines");
     if Ada.Text_IO.Is_Open (File) then
        Ada.Text_IO.Close (File);
     end if;
     Ada.Command_Line.Set_Exit_Status (Ada.Command_Line.Failure);
  when others =>
     Ada.Text_IO.Put_Line (File => Ada.Text_IO.Standard_Error,
                           Item => "Error while trying to read file: " &
                          Ada.Command_Line.Argument (Number => 1));
     if Ada.Text_IO.Is_Open (File) then
        Ada.Text_IO.Close (File);
     end if;
     Ada.Command_Line.Set_Exit_Status (Ada.Command_Line.Failure);

end Rosetta_Read;</lang>


<lang aime>void read_line(text &line, text path, integer n) {

   file f;
   f_affix(f, path);
   while (n) {

n -= 1; f_slip(f);

   f_line(f, line);


integer main(void) {

   if (2 < argc()) {

text line;

read_line(line, argv(1), 6);

o_text(line); o_byte('\n');

   return 0;



<lang AutoHotkey>FileReadLine, OutputVar, filename.txt, 7 if ErrorLevel

  MsgBox, There was an error reading the 7th line of the file</lang>


<lang awk>#!/usr/bin/awk -f

  1. usage: readnthline.awk -v lineno=6 filename


   if (lineno==0) lineno=7; ## default line number 

} {

   if (NR==lineno) print NR" : "$0;



Mmap file and search for offsets to certain line number. Since mapped file really is memory, there's no extra storage procedure once offsets are found. <lang c>#include <unistd.h>

  1. include <sys/types.h>
  2. include <sys/mman.h>
  3. include <sys/stat.h>
  4. include <fcntl.h>
  5. include <err.h>

/* following code assumes all file operations succeed. In practice,

* return codes from open, close, fstat, mmap, munmap all need to be
* checked for error.
  • /

int read_file_line(char *path, int line_no) { struct stat s; char *buf; off_t start = -1, end = -1; size_t i; int ln, fd, ret = 1;

if (line_no == 1) start = 0; else if (line_no < 1){ warn("line_no too small"); return 0; /* line_no starts at 1; less is error */ }

line_no--; /* back to zero based, easier */

fd = open(path, O_RDONLY); fstat(fd, &s);

/* Map the whole file. If the file is huge (up to GBs), OS will swap * pages in and out, and because search for lines goes sequentially * and never accesses more than one page at a time, penalty is low. * If the file is HUGE, such that OS can't find an address space to map * it, we got a real problem. In practice one would repeatedly map small * chunks, say 1MB at a time, and find the offsets of the line along the * way. Although, if file is really so huge, the line itself can't be * garanteed small enough to be "stored in memory", so there. */ buf = mmap(0, s.st_size, PROT_READ, MAP_PRIVATE, fd, 0);

/* optional; if the file is large, tell OS to read ahead */ madvise(buf, s.st_size, MADV_SEQUENTIAL);

for (i = ln = 0; i < s.st_size && ln <= line_no; i++) { if (buf[i] != '\n') continue;

if (++ln == line_no) start = i + 1; else if (ln == line_no + 1) end = i + 1; }

if (start >= s.st_size || start < 0) { warn("file does not have line %d", line_no + 1); ret = 0; } else { /* do something with the line here, like write(STDOUT_FILENO, buf + start, end - start); or copy it out, or something */ }

munmap(buf, s.st_size); close(fd);

return ret; }</lang>


<lang C sharp>using System; using System.IO;

namespace GetLine {

   internal class Program
       private static void Main(string[] args)
           Console.WriteLine(GetLine(args[0], uint.Parse(args[1])));
       private static string GetLine(string path, uint line)
           using (var reader = new StreamReader(path))
                   for (uint i = 0; i <= line; i++)
                       if (reader.EndOfStream)
                           return string.Format("There {1} less than {0} line{2} in the file.", line,
                                                ((line == 1) ? "is" : "are"), ((line == 1) ? "" : "s"));
                       if (i == line)
                           return reader.ReadLine();
               catch (IOException ex)
                   return ex.Message;
               catch (OutOfMemoryException ex)
                   return ex.Message;
           throw new Exception("Something bad happened.");



<lang go>package main

import (



func main() {

   if line, err := rsl("input.txt", 7); err == nil {
       fmt.Println("7th line:")
   } else {


func rsl(fn string, n int) (r string, err error) {

   if n < 1 {
       return "", errors.New(fmt.Sprintf("Invalid request:  line %d", n))
   f, err := os.Open(fn)
   if err != nil {
   defer f.Close()
   bf := bufio.NewReader(f)
   var l int
   for {
       line, isPrefix, err := bf.ReadLine()
       if err == io.EOF {
           switch l {
           case 0:
               return "", errors.New("Empty file.")
           case 1:
               return "", errors.New("Only 1 line.")
               return "", errors.New(fmt.Sprintf("Only %d lines", l))
       if err != nil {
           return "", err
       if l == n {
           r = string(line)
           for isPrefix {
               line, isPrefix, err = bf.ReadLine()
               if err != nil {
                   return "", err
               r += string(line)
           if r == "" {
               return "", errors.New(fmt.Sprintf("Line %d empty.", n))
       for isPrefix {
           line, isPrefix, err = bf.ReadLine()
           if err != nil {
               return "", err
   return // success


Icon and Unicon

The procedure readline uses repeated alternation (i.e. |read()) to generate the lines of the file one at a time and limitation (i.e. \ n) to limit the generation to n results. If the file is not large enough readline will fail.

While it is certainly possible to read at file at specific offsets without reading each line via seek, with files using line feed terminated variable length records something has to read the data to determine the 7th record. This solution uses a combination of repeated alternation and generation limiting to achieve this. The counter is simply to discover if there are enough records.

<lang Icon>procedure main() write(readline("",7)|"failed") end

procedure readline(f,n) # return n'th line of file f f := open(\f,"r") | fail # open file every i := n & line := |read() \ n do i -:= 1 # <== here close(f) if i = 0 then return line end</lang>


<lang j>readLine=: 4 :0

 (x-1) {:: <;.2 ] 1!:1 boxxopen y


Thus: <lang bash>$ cal 2011 > cal.txt</lang>

<lang j> 7 readLine 'cal.txt'

9 10 11 12 13 14 15  13 14 15 16 17 18 19  13 14 15 16 17 18 19</lang>

Note that this code assumes that the last character in the file is the line end character, and that the line end character is a part of the line to be retrieved.

Tacit alternative <lang j>require 'files' NB. required for versions before J701 readLineT=: <:@[ {:: 'b'&freads@]</lang> This is not quite equivalent to the code above as it handles cross-platform line-endings and those line end character(s) are removed from the result.


The code does not check if the passed file exists.
example: java -cp . LineNbr7
output : line 7: BufferedReader br = new BufferedReader(fr); <lang java>import; import;

public class LineNbr7 { public static void main(String[] args) throws Exception { FileReader fr = new FileReader(args[0]); BufferedReader br = new BufferedReader(fr);

String line = null; int lineNbr = 1; while ((line = br.readLine()) != null && lineNbr++ < 7);

if (lineNbr == 1 && line == null) { System.out.println("the file has zero length"); } else if (lineNbr == 7 && line != null) { System.out.println("line 7: " + line); } else { System.out.println("the file has only " + (lineNbr - 1) + " line(s)"); } br.close(); } }</lang>

Liberty BASIC

We read the whole file into memory, and use 'word$( string, number, delimiter)'. Line delimiter is assumed to be CRLF, and the file is assumed to exist at the path given. <lang lb>fileName$ ="F:\sample.txt" requiredLine =7

open fileName$ for input as #i

   f$ =input$( #i, lof( #i))

close #i

line7$ =word$( f$, 7, chr$( 13)) if line7$ =chr$( 13) +chr$( 10) or line7$ ="" then notice "Empty line! ( or file has fewer lines)."

print line7$</lang>


<lang Mathematica> If[# != EndOfFile , Print[#]]& @ ReadList["file", String, 7] </lang>

MATLAB / Octave

<lang Matlab>

 eln = 7;  % extract line number 7
 line = ;
 fid = fopen('foobar.txt','r');
 if (fid < 0) 

printf('Error:could not open file\n')

       n = 0; 

while ~feof(fid),

             n = n + 1; 
             if (n ~= eln), 
                   line = fgetl(fid);


 printf('line %i: %s\n',eln,line);

Insert non-formatted text here


OCaml does not provide built-in facilities to obtain a particular line from a file. It only provides a function to read one line from a file from the current position in the input channel input_line. We can use this function to get the seventh line from a file, for example as follows:

<lang ocaml>let input_line_opt ic =

 try Some (input_line ic)
 with End_of_file -> None

let nth_line n filename =

 let ic = open_in filename in
 let rec aux i =
   match input_line_opt ic with
   | Some line ->
       if i = n then begin
         close_in ic;
       end else aux (succ i)
   | None ->
       close_in ic;
       failwith "end of file reached"
 aux 1

let () =

 print_endline (nth_line 7 Sys.argv.(1))</lang>


Works with: Free_Pascal

<lang pascal>Program FileTruncate;




 filename = 'test';
 position = 7;


 myfile: text;
 line: string;
 counter: integer;


 if not FileExists(filename) then
   writeln('Error: File does not exist.');
 Assign(myfile, filename);
 counter := 0;
   if eof(myfile) then
     writeln('Error: The file "', filename, '" is too short. Cannot read line ', position);
 until counter = position - 1;
 readln(myfile, line);

end.</lang> Output:

line 7 from file test


<lang perl>#!/usr/bin/perl -s

  1. invoke as <scriptname> -n=7 [input]

while (<>) { $. == $n and print, exit } die "file too short\n";</lang>

Perl 6

<lang perl6>say lines[6] // die "Short file";</lang> Without an argument, the lines function reads filenames from the command line, or defaults to standard input. It then returns a lazy list, which we subscript to get the 7th element. Assuming this code is in a program called line7:

$ cal 2011 > cal.txt
$ line7 cal.txt
16 17 18 19 20 21 22  20 21 22 23 24 25 26  20 21 22 23 24 25 26  

This works even on infinite files because lists are lazy:

$ yes | line7


<lang PicoLisp>(in "file.txt"

  (do 6 (line))
  (or (line) (quit "No 7 lines")) )</lang>


<lang PL/I> declare text character (1000) varying, line_no fixed;

get (line_no); on endfile (f) begin;

 put skip list ('the specified line does not exist');
 go to next;


get file (f) edit ((text do i = 1 to line_no)) (L);

put skip list (text); next: ; </lang>


<lang python>from itertools import islice

with open('xxx.txt') as f:

   linelist = list(islice(f, 7, 8))
   assert linelist != [], 'Not 7 lines in file'
   line = linelist[0]</lang>


<lang purebasic>Structure lineLastRead



Procedure readNthLine(file, n, *results.lineLastRead)

 *results\lineRead = 0
 While *results\lineRead < n And Not Eof(file)
   *results\line = ReadString(file)
   *results\lineRead + 1
 If *results\lineRead = n
   ProcedureReturn 1


Define filename.s = OpenFileRequester("Choose file to read a line from", "*.*", "All files (*.*)|*.*", 0) If filename

 Define file = ReadFile(#PB_Any, filename)
 If file
   Define fileReadResults.lineLastRead, lineToRead = 7
   If readNthLine(file, lineToRead, fileReadResults)
     MessageRequester("Results", fileReadResults\line)
     MessageRequester("Error", "There are less than " + Str(lineToRead) + " lines in file.")
   MessageRequester("Error", "Couldn't open file " + filename + ".")

EndIf </lang>


<lang ruby>def getNthLine(filename, n)

 line="" do |f|
   n.times do |nr|
     line = f.gets
     if line.nil?
       puts "file #{filename} does not have #{n} lines, only #{nr}"


puts getNthLine("/etc/passwd", 7)</lang>


The code will throw a NoSuchElementException if the file doesn't have 7 lines.

<lang scala>val lines = io.Source.fromFile("input.txt").getLines val seventhLine = lines drop(6) next</lang>


The function getLine skips lines with readln and reads the requested line with getln afterwards:

<lang seed7>$ include "seed7_05.s7i";

const func string: getLine (inout file: aFile, in var integer: lineNum) is func

   var string: result is "";
   while lineNum > 1 and hasNext(aFile) do
   end while;
   result := getln(aFile);
 end func;

const proc: main is func

   var string: fileName is "input.txt";
   var file: aFile is STD_NULL;
   var string: line is "";
   aFile := open(fileName, "r");
   if aFile = STD_NULL then
     writeln("Cannot open " <& fileName);
     line := getLine(aFile, 7);
     if eof(aFile) then
       writeln("The file does not have 7 lines");
       writeln("The 7th line of the file is:");
     end if;
   end if;
 end func;</lang>


This code can deal with very large files with very long lines (up to 1 billion characters in a line should work fine, provided enough memory is available) and will return an empty string when the nth line is empty (as an empty line is still a valid line). <lang tcl>proc getNthLineFromFile {filename n} {

   set f [open $filename]
   while {[incr n -1] > 0} {
       if {[gets $f line] < 0} {
           close $f
           error "no such line"
   close $f
   return $line


puts [getNthLineFromFile example.txt 7]</lang> Where it is necessary to provide very fast access to lines of text, it becomes sensible to create an index file describing the locations of the starts of lines so that the reader code can seek directly to the right location. This is rarely needed, but can occasionally be helpful.


From the top

Variable "line" matches and takes eighth line of input: <lang txr>@(skip nil 7) @line</lang>

From the bottom

Take the third line from the bottom of the file, if it exists. <lang txr>@(skip) @line @(skip 1 2) @(eof)</lang> How this works is that the first skip will skip enough lines until the rest of the query successfully matches the input. The rest of the query matches a line, then skips two lines, and matches on EOF. So @line can only match at one location: three lines up from the end of the file. If the file doesn't have at least three lines, the query fails.


<lang tuscript>$$ MODE TUSCRIPT file="lines.txt" ERROR/STOP OPEN (file,READ,-std-) line2fetch=7

--> solution 1 ACCESS file: READ/RECORDS/UTF8 $file s,line LOOP n=1,99 READ/NEXT/EXIT file IF (n==line2fetch) PRINT line ENDLOOP ENDACCESS file

--> solution 2 line1to7=FILE (file,#line2fetch) line=SELECT (line1to7,#line2fetch) PRINT line</lang> Output: