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"?

- 158,662
- 42
- 215
- 303

- 43,506
- 91
- 209
- 269
-
I felt really misleaded by a lot of answers below. IMHO, the best one is the comment below [this answer](https://stackoverflow.com/a/4015415/6320039): no memory issue, and straight to the point. – Ulysse BN Sep 18 '19 at 16:35
7 Answers
You could get it by index from readlines
.
line = IO.readlines("file.txt")[42]
Only use this if it's a small file.

- 30,834
- 6
- 70
- 106
-
2This 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 http://stackoverflow.com/questions/25189262/why-is-slurping-a-file-bad which contains benchmarks. – the Tin Man Dec 14 '15 at 18:11
-
I was surprised to learn that it takes as much as a couple of MB! – Jonas Elfström Dec 15 '15 at 07:42
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

- 34,866
- 14
- 107
- 142
-
1
-
-
Thanks for `[*File.open('…')]`, did not know that `to_a` for File instance can give me its lines – tig Oct 25 '10 at 14:24
-
@Nakilon: file lines are indexed from 1 (all editors and even `cat -n` do so) – tig Oct 25 '10 at 14:26
-
Regarding #2: I think unsplat is costly operation. Also, syntax is confusing. – Maxim Kulkin Oct 26 '10 at 09:56
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.

- 54,010
- 13
- 102
- 111
-
Hehe, that's what you get from fellow Rubyists when you try to write optimized code eh? :) – August Lilleaas Oct 25 '10 at 12:30
-
-
4I'd argue that reading an entire 20MB file into memory to get one line from it is bad practice in any language. – August Lilleaas Oct 25 '10 at 12:38
-
..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 Oct 25 '10 at 12:45
-
1@August Lilleaas, i'd argue, that here you don't need `result = nil`, `return` and write C-style loops, when you have `times`. – Nakilon Oct 25 '10 at 13:10
-
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 Oct 25 '10 at 20:50
-
-
2Thankfully, 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 Oct 27 '10 at 11:56
-
5@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. – Joshua Grosso Reinstate CMs Feb 05 '16 at 18:42
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

- 158,662
- 42
- 215
- 303

- 79,051
- 16
- 138
- 171
-
2You don't really need lineno(). You could replace the 'until' line with `(lineno-1).times {f.gets}`. – Mark Thomas Oct 25 '10 at 14:44
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

- 327,991
- 56
- 259
- 343
-
-
-
Doesn't matter. If the line number is last 2nd line for example, it has to read until that line as well... – ghostdog74 Oct 25 '10 at 14:35
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

- 2,698
- 22
- 11
These solutions work if you want only one line from a file, or if you want multiple lines from a file small enough to be read repeatedly. Large files (for example, 10 million lines) take much longer to search for a specific line so it's better to get the necessary lines sequentially in a single read so the large file doesn't get read multiple times.
Create a large file:
File.open('foo', 'a') { |f| f.write((0..10_000_000).to_a.join("\n")) }
Pick which lines will be read from it and make sure they're sorted:
lines = [9_999_999, 3_333_333, 6_666_666].sort
Print out those lines:
File.open('foo') do |f|
lines.each_with_index do |line, index|
(line - (index.zero? ? 0 : lines[index - 1]) - 1).times { f.gets }
puts f.gets
end
end
This solution works for any number of lines, does not load the entire file into memory, reads as few lines as possible, and only reads the file one time.

- 9,815
- 3
- 33
- 52