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
views:
107answers:
7
+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
2010-10-25 12:14:52
Is solution #2 getting line n+1?
Mark Thomas
2010-10-25 13:18:20
@Mark Thomas, in #1 too. I supposed indexing from 0.
Nakilon
2010-10-25 13:25:06
Thanks for `[*File.open('…')]`, did not know that `to_a` for File instance can give me its lines
tig
2010-10-25 14:24:53
@Nakilon: file lines are indexed from 1 (all editors and even `cat -n` do so)
tig
2010-10-25 14:26:28
@tig, ok, I'll keep it in mind. And edit answer.
Nakilon
2010-10-25 14:28:46
Regarding #2: I think unsplat is costly operation. Also, syntax is confusing.
Maxim Kulkin
2010-10-26 09:56:21
+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
2010-10-25 12:19:20
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
2010-10-25 12:21:36
Hehe, that's what you get from fellow Rubyists when you try to write optimized code eh? :)
August Lilleaas
2010-10-25 12:30:51
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
2010-10-25 12:38:39
..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
2010-10-25 12:45:38
@August Lilleaas, i'd argue, that here you don't need `result = nil`, `return` and write C-style loops, when you have `times`.
Nakilon
2010-10-25 13:10:17
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
2010-10-25 20:50:47
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
2010-10-27 11:56:59
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
2010-10-25 13:05:43
Doesn't matter. If the line number is last 2nd line for example, it has to read until that line as well...
ghostdog74
2010-10-25 14:35:38
+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
2010-10-25 14:15:02
You don't really need lineno(). You could replace the 'until' line with `(lineno-1).times {f.gets}`.
Mark Thomas
2010-10-25 14:44:58
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
2010-10-25 14:57:25
@Maxim, that's true. The last example could perhaps be `f=open(file); puts f.get_line(20); f.close`
Mark Thomas
2010-10-26 11:35:44
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
2010-10-26 09:48:38