Passing an gets.chomp as an argument - ruby

input_file = ARGV.first
def print_all(f)
puts f.read
end
def rewind(f)
f.seek(0)
end
def print_a_line(line_count, f)
puts "#{line_count}, #{f.gets.chomp}"
end
current_file = open(input_file)
puts "First let's print the whole file:¥n"
print_all(current_file)
puts "Let's rewind kind a like a tape"
rewind(current_file)
puts "Let's print three lines:"
current_line = 1
print_a_line(current_line, current_file)
current_line += 1
print_a_line(current_line, current_file)
I'm sure there is a kinda similar post to this, but my question is a bit different. As seen above, the print_a_line method got two params that are line_count and f.
1) As I understood, line_count argument only serves as a variable which is current_line and it is just an integer. How does it relate to the rewind(f) method because when I run the code, the method print_a_line shows this:
1, Hi
2, I'm a noob
where 1 is the first line and 2 is the second. line_count is just a number, how does ruby know that 1 is line 1 and 2 is line 2?
2) Why use gets.chomp in method print_a_line? If I pass just f like this
def print_a_line(line_count, f)
puts "#{line_count}, #{f}"
end
I'll get a crazy result which is
1, #<File:0x007fccef84c4c0>
2, #<File:0x007fccef84c4c0>

Because IO#gets reads next line from readable I/O stream(in this case it's a file) and returns a String object when reading successfully and not reached end of the file. And, chomp removes carriage return characters from String object.
So when you have a file with content such as:
This is file content.
This file has multiple lines.
The code will print:
1, This is file content.
2, This file has multiple lines.
In the second case, you're passing file object itself and not reading it. Hence you see those objects in output.

Related

undefined method `chomp' for nil:NilClass (NoMethodError)

I have entered the following code from Zed Shaw's book on "Learning Ruby the Hard Way
input_file = ARGV.first #this takes the file test.txt
def print_all(f) #reading a line
puts f.read
end
def rewind(f)
f.seek(0)
end
def print_a_line(line_count, f)
current_line
puts "#{line_count}, #{f.gets.chomp}"
end
current_file = open(input_file)
puts "First let's print the whole file:\n"
print_all(current_file)
puts "Now let's rewind, kind of like a tape"
rewind(current_file)
puts "Let's print three line:"
current_line = 1
print_a_line(current_line, current_file)
current_line = current_line + 1
print_a_line(current_line, current_file)
current_line = current_line + 1
print_a_line(current_line, current_file)
The error I am getting is 'ex20.rb:12:in print_a_line': undefined method chomp' for nil:NilClass (NoMethodError)
from ex20.rb:31:in `'
Any help would be greatly appreciated. I have followed his example word by word.
You have to add a few more lines to the test.txt file (at least three lines of text for each of the method calls you do at the end).
I ran across the same issue because the lesson isn't exactly clear about it, but since the script prints out three lines in a row, you need 3 lines of text in the file for the script to work.
add more lines to your test.txt file

LRTHW exercise 20 strange output

For this exercise I wrote all the code perfectly and even double checked by literally copying and pasting the code from the book (Learn Ruby the Hard Way). For some reason when I call the print_a_line() function, it won't print out the current_line argument I pass through until the 3rd call where it will print out 3 before the line. Is there some flushing of the IO stream I am missing out on here or some nuance with Powershell?
I am running Ruby 2.0.0p576 (x64) on Windows 8 64 bit machine.
Code:
input_file = ARGV.first
def print_all(f)
puts f.read
end
def rewind(f)
f.seek(0)
end
def print_a_line(line_count, f)
puts "#{line_count}, #{f.gets.chomp}"
end
current_file = open(input_file)
puts "First let's print the whole file:\n"
print_all(current_file)
puts "Now let's rewind, kind of like a tape."
rewind(current_file)
puts "Let's print three lines:"
current_line = 1
print_a_line(current_line, current_file)
current_line = current_line + 1
print_a_line(current_line, current_file)
current_line = current_line + 1
print_a_line(current_line, current_file)
I deleted the test.txt file and recreated it from scratch and this made the code work perfectly. I am not 100% as to why, but I have a hunch that from reusing that file in many different tests and playing around in Powershell I had inadvertently left some strange formatting in the file causing it to behave differently.

find the target string from a large file

I want to write a class, it can find a target string in a txt file and output the line number and the position.
class ReadFile
def find_string(filename, string)
line_num = 0
IO.readlines(filename).each do |line|
line_num += 1
if line.include?(string)
puts line_num
puts line.index(string)
end
end
end
end
a= ReadFile.new
a.find_string('test.txt', "abc")
If the txt file is very large(1 GB, 10GB ...), the performance of this method is very poor.
Is there the better solution?
Use foreach to efficiently read a single line from the file at a time and with_index to track the line number (0-based):
IO.foreach(filename).with_index do |line, index|
if found = line.index(string)
puts "#{index+1}, #{found+1}"
break # skip this if you want to find more than 1 result
end
end
See here for a good explanation of why readlines is giving you performance problems.
This is a variant of #PinnyM's answer. It uses find, which I think is more descriptive than looping and breaking, but does the same thing. This does have a small penalty of having to determine the offset into the line where the string begins after the line is found.
line, index = IO.foreach(filename).with_index.find { |line,index|
line.include?(string) }
if line
puts "'#{string}' found in line #{index}, " +
"beginning in column #{line.index(string)+1}"
else
puts "'#{string}' not found"
end

What does IO do?

What does the (0, IO::SEEK_SET) do in method rewind? I understand the rest of the code, but I am honestly stuck at the (0, IO::SEEK_SET).
input_file = ARGV[0]
def print_all(f)
puts f.read()
end
def rewind(f)
f.seek(0, IO::SEEK_SET)
end
def print_a_line(line_count, f)
puts "#{line_count} #{f.readline()}"
end
current_file = File.open(input_file)
puts "First let's print the whole file:"
puts # a blank line
print_all(current_file)
puts "Now let's rewind, kind of like a tape."
rewind(current_file)
puts "Let's print three lines:"
current_line = 1
print_a_line(current_line, current_file)
current_line = current_line + 1
print_a_line(current_line, current_file)
current_line = current_line + 1
print_a_line(current_line, current_file)
seek(amount, whence=IO::SEEK_SET) → 0
Seeks to a given offset amount in the stream according to the value of whence:
:CUR or IO::SEEK_CUR | Seeks to _amount_ plus current position
----------------------+--------------------------------------------------
:END or IO::SEEK_END | Seeks to _amount_ plus end of stream (you
| probably want a negative value for _amount_)
----------------------+--------------------------------------------------
:SET or IO::SEEK_SET | Seeks to the absolute location given by _amount_
http://www.ruby-doc.org/core-2.1.2/IO.html#method-i-seek
When you specify a position to seek to, there are typically three ways to do this
SEEK_SET: from the start of the file (ie: absolute)
SEEK_CUR: from the current position (ie: relative)
SEEK_END: from the end of the file
So in your case the rewind method simply seeks to the beginning of the file.

Determine last line in Ruby

I'm wondering how I can determine when I am on the last line of a file that I reading in. My code looks like
File.open(file_name).each do |line|
if(someway_to_determine_last_line)
end
I noticed that there is a file.eof? method, but how would I call the method as the file is being read? Thanks!
If you're iterating the file with each, then the last line will be passed to the block after the end-of-file is reached, because the last line is, by definition, the line ending with EOF.
So just call file.eof? in the block.
If you'd like to determine if it's the last non-empty line in the file, you'd have to implement some kind of readahead.
Depending on what you need to do with this "last non-empty line", you might be able to do something like this:
last_line = nil
File.open(file_name).each do |line|
last_line = line if(!line.chomp.empty?)
# Do all sorts of other things
end
if(last_line)
# Do things with the last non-empty line.
end
Secret sauce is .to_a
lines = File.open(filename).to_a
Get the first line:
puts lines.first
Get the last line:
puts lines.last
Get the n line of a file:
puts lines.at(5)
Get the count of lines:
puts lines.count
fd.eof? works, but just for fun, here's a generic solution that works with any kind of enumerators (Ruby 1.9):
class Enumerator
def +(other)
Enumerator.new do |yielder|
each { |e| yielder << e }
other.each { |e| yielder << e }
end
end
def with_last
Enumerator.new do |yielder|
(self + [:some_flag_here]).each_cons(2) do |a, b|
yielder << [a, b == :some_flag_here]
end
end
end
end
# a.txt is a file containing "1\n2\n3\n"
open("a.txt").lines.with_last.each do |line, is_last|
p [line, is_last]
end
Which outputs:
["1\n", false]
["2\n", false]
["3\n", true]
Open your file and use the readline method:
To simply manipulate last line of file do the following:
f = File.open('example.txt').readlines
f.each do |readline|
if readline[f.last]
puts "LAST LINE, do something to it"
else
puts "#{readline} "
end
end
Line 1 reads the file in as an array of lines
Line 2 uses that object and iterates over each of them
Line 3 tests if the current line matches the last line
Line 4 acts if it's a match
Line 5 & 6 handle behavior for non-matching circumstance

Resources