Reading a file by passing filename to a function - ruby

I am trying to read the file name and process each line. How do I pass the file name to the the function?
puts "file name?? "
_file = get.chomps
def printFile(_file)
do |f|
f.each_line do |line|
print_me = "Line 1 " + line
return print_me
end
end
end
I am planning to pass print_me to another function like:
def thisWillPrint(print_me)
new_print = print_me + " DONE! "
end

I can see a few problems in your code. First you are using a block inside the definition of your printFile function which is a syntax error, next you use the variable f in that block which was never given a value, on top of that you try to do a loop on it and never open a file descriptor. Finally you must call the printFile function somewhere so that ruby knows it has to run it.
The first thing your printFile function should do is get a file descriptor to the file the user gives you as a string in the _file variable, this way you actually have a stream you can read lines from not just the string object. So I recommend you change the variable from _file to fileName, and leave file for the stream. You do this by using Ruby's own File class and calling its open method. As you can see from the documentation open can be called in a few different ways, but let's use a block like you were trying to do.
puts 'give me a path to a file'
fileName = gets.chomp
def printFile(fileName)
counter = 0
File.open(fileName) do |file|
while line = file.gets
print_me = "line " + counter.to_s + " "+line
thisWillPrint(print_me)
end
end
end
def thisWillPrint(print_me)
puts print_me + " DONE! "
end
printFile(fileName)
You also have to call the printFile function at the end so that ruby actually runs something.

Note that by returning inside the loop, you will exit it as well. With the following, you will get the contents of the file.
def printfile(filename)
print_me = ""
File.open(filename, "r") do |f|
f.each {|line| print_me << line }
end
print_me
end
For large files, the return variable will also be very large.

To read a line from standard input you can use the gets method. The gets method captures a newline \n by default. You have to use the chomp method to get rid of the newline.
So to get the file's name from standard input you can do the following:
print "File's name? "
_file = gets.chomp
Inside the printFile method you can do the following:
def printFile(_file)
print_me = ""
File.foreach(_file) do |line|
# process each line however you'd like inside this block
# for example:
print_me += line
end
return print_me # explicit return not required
end
Note that you do not have to explicity 'return' something if it's the last expression in the method. The last expression could have just been print_me instead.
You can pass what this method returns to another method like thisWillPrint like this:
def thisWillPrint(print_me)
new_print = print_me + "Done!"
end
output = printFile(_file)
thisWillPrint(output)

Related

How to display contents of file

I'm trying to display the contents of a file.
class CommonLog
def initialize(filename)
#filename = filename
end
def readfile
text = File.readlines(#filename).map do |line|
line.split(" ")
puts text
end
end
end
my_file = CommonLog.new("test.log")
puts my_file.readfile
I am receiving the error No such file or directory - test.log".
Create the file. But once you get past that error where the file does not exist, you are doing things that are not worthwhile.
You are splitting the line, but not storing or using the information.
You are using puts to output the return of the text object, but your question title is how to view the contents.
You can puts line which will output each line of the file once it exists.
You are assigning text, and referencing it in the same statement, and so when you puts text it is nil. The method will return the assignment to text, but otherwise it is not being used.
If you remove the reference to text inside the block, you get get rid of the assignment to text variable.
Finally, you can remove the entire block if you would simply want an array of lines to be returned, or use join on the readlines method.
The redesigned version may look like this:
class CommonLog
def initialize(filename)
#filename = filename
end
def readfile
File.readlines(#filename)
end
end
my_file = CommonLog.new("test.log")
puts my_file.readfile # This actually prints an Array
That array holds the contents of your file, and the puts displays the contents of that file as stored in that array.

How do I detect end of file in Ruby?

I wrote the following script to read a CSV file:
f = File.open("aFile.csv")
text = f.read
text.each_line do |line|
if (f.eof?)
puts "End of file reached"
else
line_num +=1
if(line_num < 6) then
puts "____SKIPPED LINE____"
next
end
end
arr = line.split(",")
puts "line number = #{line_num}"
end
This code runs fine if I take out the line:
if (f.eof?)
puts "End of file reached"
With this line in I get an exception.
I was wondering how I can detect the end of file in the code above.
Try this short example:
f = File.open(__FILE__)
text = f.read
p f.eof? # -> true
p text.class #-> String
With f.read you read the whole file into text and reach EOF.
(Remark: __FILE__ is the script file itself. You may use you csv-file).
In your code you use text.each_line. This executes each_line for the string text. It has no effect on f.
You could use File#each_line without using a variable text. The test for EOF is not necessary. each_line loops on each line and detects EOF on its own.
f = File.open(__FILE__)
line_num = 0
f.each_line do |line|
line_num +=1
if (line_num < 6)
puts "____SKIPPED LINE____"
next
end
arr = line.split(",")
puts "line number = #{line_num}"
end
f.close
You should close the file after reading it. To use blocks for this is more Ruby-like:
line_num = 0
File.open(__FILE__) do | f|
f.each_line do |line|
line_num +=1
if (line_num < 6)
puts "____SKIPPED LINE____"
next
end
arr = line.split(",")
puts "line number = #{line_num}"
end
end
One general remark: There is a CSV library in Ruby. Normally it is better to use that.
https://www.ruby-forum.com/topic/218093#946117 talks about this.
content = File.read("file.txt")
content = File.readlines("file.txt")
The above 'slurps' the entire file into memory.
File.foreach("file.txt") {|line| content << line}
You can also use IO#each_line. These last two options do not read the entire file into memory. The use of the block makes this automatically close your IO object as well. There are other ways as well, IO and File classes are pretty feature rich!
I refer to IO objects, as File is a subclass of IO. I tend to use IO when I don't really need the added methods from File class for the object.
In this way you don't need to deal with EOF, Ruby will for you.
Sometimes the best handling is not to, when you really don't need to.
Of course, Ruby has a method for this.
Without testing this, it seems you should perform a rescue rather than checking.
http://www.ruby-doc.org/core-2.0/EOFError.html
file = File.open("aFile.csv")
begin
loop do
some_line = file.readline
# some stuff
end
rescue EOFError
# You've reached the end. Handle it.
end

Memorize gets.chomp

I'm trying to make a text editor in Ruby, but I don't know how to memorize an input with gets.chomp.
Here is my code so far:
outp =
def tor
text = gets.chomp
outp = "#{outp}" += "#{text}"
puts outp
end
while true
tor
end
Ordinary variables , like outp, in a method are only visible (AKA have scope) inside that method.
a = "aaa"
def x
puts a
end
x # =>error: undefined local variable or method `a' for main:Object
Why is that? For one thing, if you are writing a method and you need a counter, you can use a variable named i (or whatever) without worrying about other variables named i outside your method.
But... you want to interact with an outside variable in your method! This is one way:
#outp = "" # note the "", initializing #output to an empty string.
def tor
text = gets.chomp
#outp = #outp + text #not "#{#output}"+"#{text}", come on.
puts #outp
end
while true
tor
end
The # gives this variable a greater visisbility (scope).
This is another way: pass the variable as an argument. It is as saying to your method: "Here, work with this.".
output = ""
def tor(old_text)
old_text + gets.chomp
end
loop do #just another way of saying 'while true'
output = tor(output)
puts output
end

loop, array and file problem in ruby

I'm currently learning ruby and here what I'm trying to do:
A script which open a file, make a subsitution, then comparing every lines to each other to see if it exist many times.
So, I tried to work directly with the string, but I didn't find how to do it, so I put every line in an array, and comparing every row.
But I got a first problem.
Here is my code:
#!/usr/bin/env ruby
DOC = "test.txt"
FIND = /,,^M/
SEP = "\n"
#make substitution
puts File.read(DOC).gsub(FIND, SEP)
#open the file and put every line in an array
openFile = File.open(DOC, "r+")
fileArray = openFile.each { |line| line.split(SEP) }
#print fileArray #--> give the name of the object
#Cross the array to compare every items to every others
fileArray.each do |items|
items.chomp
fileArray.each do |items2|
items2.chomp
#Delete if the item already exist
if items = items2
fileArray.delete(items2)
end
end
end
#Save the result in a new file
File.open("test2.txt", "w") do |f|
f.puts fileArray
end
At the end, I only have the name of the array object "fileArray". I print the object after the split, and i've got the same, so I guess the problem is from here. Little help required (if you know how to do this without array, just with the line in the file, answer appreciate too).
Thanks !
EDIT:
So, here's my code now
#!/usr/bin/env ruby
DOC = "test.txt"
FIND = /,,^M/
SEP = "\n"
#make substitution
File.read(DOC).gsub(FIND, SEP)
unique_lines = File.readlines(DOC).uniq
#Save the result in a new file
File.open('test2.txt', 'w') { |f| f.puts(unique_lines) }
Can't figure out how to chomp this.
Deleting duplicate lines in a file:
no_duplicate_lines = File.readlines("filename").uniq
No need to write so much code :)
Modify your code like this:
f.puts fileArray.join("\n")
Alternate way:
unique_lines = File.readlines("filename").uniq
# puts(unique_lines.join("\n")) # Uncomment this line and see if the variable holds the result you want...
File.open('filename', 'w') {|f| f.puts(unique_lines.join("\n"))}
Just a couple of points about the original code:
fileArray = openFile.each { |line| line.split(SEP) }
sets fileArray to a File object, which I suspect wasn't your intention. File#each (the # notation is Ruby convention to describe a particular method on an object of the supplied class) executes your supplied block for each line (it's also available with a synonym: each_line), where a line is defined by default as your OS's end-line character(s).
If you were looking to build an array of lines, then you could just have written
fileArray = openFile.readlines
and if you wanted those lines to be chomped (often a good idea) then that could be achieved by something like
fileArray = openFile.readlines.collect { |line| line.chomp }
or even (since File mixes in Enumerable)
fileArray = openFile.collect { |line| line.chomp }
And one other tiny thing: Ruby tests for equality with ==, = is only for assignment, so
if items = items2
will set items to items2 (and will always evaluate as true)

private method `chomp' called for nil:NilClass (NoMethodError)

I am attempting to learn Ruby by converting a Java program to Ruby, but I've been coming up with an error surrounding this block of code:
def create
#user_input = String.new()
# #word_arr = Array.new
print "Enter the text to be converted to pig latin, EOF to quit: "
while gets do
STDOUT.flush
#user_input = gets.chomp
#word_arr = #user_input.string.split(' ')
#word_arr.each { |x| puts x.engToLatin() + ' '}
print "EOF to Quit"
#user_input = ""
end
end
I've been getting this error:
EnglishToPigLatin.rb:14:in `create': private method `chomp' called for nil:NilClass (NoMethodError)
from EnglishToPigLatin.rb:60
This is the area around line 60:
#if __FILE__ == $0
mg = EnglishToPigLatin.new
mg.create
#end
Essentially what I am trying to do is while there is still input, get that input, split it up into individual words, and run each word through a Pig Latin conversion method.
It looks like you're trying to get input inside of your loop.
Try
loop do
user_input = gets.chomp!
word_arr = user_input.to_s.split(' ')
word_arr.each { |x| puts x.engToLatin() + ' '}
puts "EOF to Quit"
end
Otherwise you're trying to get the next line of input when there isn't one. Additionally, do isn't necessary for a while statement.
You also don't need to reset #user_input to ''.
And since this is all in a block, you don't need to use instance variables, unless the methods you call need them.
Also your conditional is always true. gets will block until it gets a line of input. You can use loop for an infinite loop that ends on an interrupt.
Also, you needn't flush STDOUT if you use a puts for the last line there instead of a print.
The whole thing could be a script or a method in a module. An instance doesn't even need to be made. And if you do, instead of using two lines with your mg.create, you should define an initialize method. This is used as a constructor then, and whatever you set when you create an instance should be put there.
It can all be done like this:
loop do
puts gets.chomp.split(' ').map{ |x| x.engToLatin() }.join(' ')
puts "EOF to Quit"
end
Mario's answer is right. But I have the following notes.
You can still use the while construction as below.
+' ' implies that you don't want line breaks after each word. I changed that part. map and join is common in similar cases. print does not add a line break while puts does.
I am not sure what you are trying to do with STDOUT.flush. If you wanted to scroll to the top of the screen before each output, use system('clear').
You have a method entToLatin, and it should work, but it is a ruby convention to use underscore, like eng_to_latin for methods (although there are a few exceptions).
So a more rubyish way would be:
def create
print "Enter the text to be converted to pig latin, EOF to quit: "
while input = gets.strip and input != 'EOF'
system('clear')
puts input.split(/\s+/).map{|x| x.engToLatin}.join(' ')
puts "EOP to Quit"
end
end
And if you are using ruby 1.9.2, you can shorten map so that:
def create
print "Enter the text to be converted to pig latin, EOF to quit: "
while input = gets.strip and input != 'EOF'
system('clear')
puts input.split(/\s+/).map(:engToLatin).join(' ')
puts "EOP to Quit"
end
end

Resources