tags:

views:

107

answers:

7

Is it possible to extract a particular line from a file knowing it's line number? e.g just get the contents of line N as a string from file text.txt

+2  A: 

Try one of these two solutions:

f = File.open('file.txt')

#1 solution
p [*f][n-1]

#2 solution
n.times{f.gets}
p $_

f.close

But I don't sure about how much memory will be used in #1.

Nakilon
Is solution #2 getting line n+1?
Mark Thomas
@Mark Thomas, in #1 too. I supposed indexing from 0.
Nakilon
Thanks for `[*File.open('…')]`, did not know that `to_a` for File instance can give me its lines
tig
@Nakilon: file lines are indexed from 1 (all editors and even `cat -n` do so)
tig
@tig, ok, I'll keep it in mind. And edit answer.
Nakilon
Regarding #2: I think unsplat is costly operation. Also, syntax is confusing.
Maxim Kulkin
+1  A: 

You could get it by index from readlines.

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

Only use this if it's a small file.

Jonas Elfström
A: 
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.

August Lilleaas
You wrote in C too long...
Nakilon
Hehe, that's what you get from fellow Rubyists when you try to write optimized code eh? :)
August Lilleaas
In different languages you must optimize different things.
Nakilon
I'd argue that reading an entire 20MB file into memory to get one line from it is bad practice in any language.
August Lilleaas
..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..
August Lilleaas
@August Lilleaas, i'd argue, that here you don't need `result = nil`, `return` and write C-style loops, when you have `times`.
Nakilon
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.
August Lilleaas
@August Lilleaas, I think, Ruby is not your language of life ..)
Nakilon
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 :)
August Lilleaas
A: 
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
ghostdog74
Nice. That's when global $-variables become useful.
Nakilon
This keeps on reading the file when the line is already found.
steenslag
Doesn't matter. If the line number is last 2nd line for example, it has to read until that line as well...
ghostdog74
+1  A: 

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
steenslag
You don't really need lineno(). You could replace the 'until' line with `(lineno-1).times {f.gets}`.
Mark Thomas
A: 

Here's a very small method (probably too small, it sacrifices some readability)

def get_line(file, n) 
  open(file) do |f|; n.times {f.gets}; end; $_
end

EDIT: Here's another way:

class File
  def get_line(n)
    n.times{self.gets}
    $_
  end
end

Then you can do:

open(file).get_line(20)
Mark Thomas
Some? :) Ruby QuickRef to the rescue!
Jonas Elfström
Bad solution: the file wasn't close afterwards.
Maxim Kulkin
@Maxim, that's true. The last example could perhaps be `f=open(file); puts f.get_line(20); f.close`
Mark Thomas
A: 

If you want one liner and do not care about memory usage, use (assuming lines are numbered from 1)

lineN = IO.readlines('text.txt')[n-1]

or

lineN = f.readlines[n-1]

if you already have file opened.

Otherwise it would be better to do like this:

lineN = File.open('text.txt') do |f|
          (n-1).times { f.gets } # skip lines preceeding line N
          f.gets                 # read line N contents
        end
Maxim Kulkin