How to get a particular line from a file

23,823

Solution 1

Try one of these two solutions:

file = File.open "file.txt"

#1 solution would eat a lot of RAM
p [*file][n-1]

#2 solution would not
n.times{ file.gets }
p $_

file.close

Solution 2

You could get it by index from readlines.

line = IO.readlines("file.txt")[42]

Only use this if it's a small file.

Solution 3

def get_line_from_file(path, line)
  result = nil

  File.open(path, "r") do |f|
    while line > 0
      line -= 1
      result = f.gets
    end
  end

  return result
end

get_line_from_file("/tmp/foo.txt", 20)

This is a good solution because:

  • You don't use File.read, thus you don't read the entire file into memory. Doing so could become a problem if the file is 20MB large and you read often enough so GC doesn't keep up.
  • You only read from the file until the line you want. If your file has 1000 lines, getting line 20 will only read the 20 first lines into Ruby.

You can replace gets with readline if you want to raise an error (EOFError) instead of returning nil when passing an out-of-bounds line.

Solution 4

File has a nice lineno method.

def get_line(filename, lineno)
  File.open(filename,'r') do |f|
     f.gets until f.lineno == lineno - 1
     f.gets
  end
end

Solution 5

linenumber=5
open("file").each_with_index{|line,ind|
  if  ind+1==linenumber
    save=line
    # break or exit if needed.
  end
}

or

linenumber=5
f=open("file")
while line=f.gets
  if $. == linenumber # $. is line number
    print "#{f.lineno} #{line}" # another way
    # break  # break or exit if needed
  end
end
f.close

If you just want to get the line and do nothing else, you can use this one liner

ruby -ne '(print $_ and exit) if $.==5' file
Share:
23,823

Related videos on Youtube

Skizit
Author by

Skizit

Hi!

Updated on July 09, 2022

Comments

  • Skizit
    Skizit almost 2 years

    Is it possible to extract a particular line from a file knowing its line number? For example, just get the contents of line N as a string from file "text.txt"?

    • Ulysse BN
      Ulysse BN over 4 years
      I felt really misleaded by a lot of answers below. IMHO, the best one is the comment below this answer: no memory issue, and straight to the point.
  • Bushra Khan
    Bushra Khan over 13 years
    Hehe, that's what you get from fellow Rubyists when you try to write optimized code eh? :)
  • Nakilon
    Nakilon over 13 years
    In different languages you must optimize different things.
  • Bushra Khan
    Bushra Khan over 13 years
    I'd argue that reading an entire 20MB file into memory to get one line from it is bad practice in any language.
  • Bushra Khan
    Bushra Khan over 13 years
    ..I'd also argue that get_line_from_file is a terrible method name. And that the implementation itself could be rewritten to be more ruby-like. But I digress..
  • Nakilon
    Nakilon over 13 years
    @August Lilleaas, i'd argue, that here you don't need result = nil, return and write C-style loops, when you have times.
  • Mark Thomas
    Mark Thomas over 13 years
    Is solution #2 getting line n+1?
  • Nakilon
    Nakilon over 13 years
    @Mark Thomas, in #1 too. I supposed indexing from 0.
  • Nakilon
    Nakilon over 13 years
    Nice. That's when global $-variables become useful.
  • steenslag
    steenslag over 13 years
    This keeps on reading the file when the line is already found.
  • tig
    tig over 13 years
    Thanks for [*File.open('…')], did not know that to_a for File instance can give me its lines
  • tig
    tig over 13 years
    @Nakilon: file lines are indexed from 1 (all editors and even cat -n do so)
  • ghostdog74
    ghostdog74 over 13 years
    Doesn't matter. If the line number is last 2nd line for example, it has to read until that line as well...
  • Mark Thomas
    Mark Thomas over 13 years
    You don't really need lineno(). You could replace the 'until' line with (lineno-1).times {f.gets}.
  • Bushra Khan
    Bushra Khan over 13 years
    It's a matter of preference. I prefer to explicitly have a return for longer methods where you're supposed to use the return value. I think it increases clarity by making the usage of the method clearer.
  • Maxim Kulkin
    Maxim Kulkin over 13 years
    Regarding #2: I think unsplat is costly operation. Also, syntax is confusing.
  • Nakilon
    Nakilon over 13 years
    @August Lilleaas, I think, Ruby is not your language of life ..)
  • Bushra Khan
    Bushra Khan over 13 years
    Thankfully, I can write as much ruby as I want, the way I want, and I don't have to listen to what cargo culters want me to do :)
  • Joseph Ravenwolfe
    Joseph Ravenwolfe over 11 years
    Thanks for this answer, exactly what I was looking for.
  • PJP
    PJP over 8 years
    This is only the right answer if the file is small, less than a couple MB. Otherwise it'll force Ruby to load the entire file at once, which, in the case of a large file is slower than using a foreach or gets based solution. See stackoverflow.com/questions/25189262/why-is-slurping-a-file-‌​bad which contains benchmarks.
  • Jonas Elfström
    Jonas Elfström over 8 years
    I was surprised to learn that it takes as much as a couple of MB!
  • Joshua Grosso Reinstate CMs
    Joshua Grosso Reinstate CMs about 8 years
    @AugustLilleaas when you use a language, however, you should write it idiomatically, if only for the reason that languages often optimize idiomatic constructs more than other ones. Also, if anyone's ever going to maintain your code, it's a disservice to not learn the right way to do it.