Reading file input with ruby via command line input - ruby

So I am trying to read a file in ruby by giving the name via the command line. So far my code reads as follows:
puts "What is the name of the file to read?"
fileName = gets.chomp
file = $stdin.read.strip
f = File.open(file, “r”)
f.each_line { |line|
puts line
}
What I see happening is it is reading the inputs through the command line but does not read a file. For example, I can pass 'input.txt', 'code.txt', and 'sonic.txt' as file names but the program just loops back seeking another input. How can I change this to read the file by name and then out put the contents of that file?

Your problems are:
The line fileName = gets.chomp is useless. Remove that.
file = $stdin.read.strip will not let you terminate the input. Use gets to get user's input from the command line.
You are using the wrong quotation “ in your parameter “r” for File.open.
You are not closing the file after reading it. It is better to use the block form of File.open to ensure the file is closed after using.
Here is a minimum fix:
puts "What is the name of the file to read?"
file = gets.chomp
File.open(file, "r"){|f|
f.each_line {|line|
puts line
}
}

Related

How would you close this file descriptor?

Let's say you have the following code:
from_file, to_file = ARGV
puts "Copying from #{from_file} to #{to_file}"
#in_file = open(from_file)
#indata = in_file.read
indata = open(from_file).read # Combined in_file and indata.
puts "The input file is #{indata.length} bytes long."
puts "Does the output file exist? #{File.exist?(to_file)}"
puts "Ready, hit RETURN to continue or CTRL-C to abort."
$stdin.gets
out_file = open(to_file, 'w')
out_file.write(indata)
puts "Alright, all done."
out_file.close
#in_file.close
How would you close the file descriptor invoked by indata? You will need to close File open, but indata is really a (File open).read.
P.S. Since it's a script, it will be closed automatically upon exit. Let's assume that we're running a general, consistently running backend service. And we don't know whether garbage collector will kick in, so we will need to explicitly close it. What would you do?
If you are just copying the file...
you could just use FileUtils#cp:
FileUtils.cp("from_file", "to_file")
or even shell-out to the operating system and do it with a system command.
Let's suppose you want to do something to the input file before writing it to the output file.
If from_file is not large,...
you could "gulp it" into a string using IO.read:
str = IO.read(from_file)
manipulate str as desired, to obtain new_str, then then blast it to the output file using IO#write:
IO.write("to_file", new_str)
Note that for the class File:
File < IO #=> true # File inherits IO's methods
which is why you often see this written File.read(...) and File.write(...).
If from_file is large, read a line, write a line...
provided the changes to be made are done for each line separately.
f = File.open("to_file", "w") # or File.new("to_file", "w")
IO.foreach("from_file") do |line|
# < modify line to produce new_line >
f.puts new_line
end
f.close
foreach closes "from_file" when it's finished. If f.close is not present, Ruby will close "to_file" when the method containing the code goes out of scope. Still, it's a good idea to close it in case other work is done before the code goes out of scope.
Passing File.open a block is generally a nice way to go about things, so I’ll offer it up as an alternative even if it doesn’t seem to be quite what you asked.
indata = File.open(from_file) do |f|
f.read
end

Why can't I use `filename.open' instead of `open(filename)'?

In this piece of code which lets you read a file in the terminal, why do you need to use open(filename) rather than filename.open?
filename = ARGV.first
txt = open(filename)
puts "Here's your file #{filename}:"
print txt.read
print "Type the filename again: "
file_again = $stdin.gets.chomp
txt_again = open(file_again)
print txt_again.read
You cant use filename.open, because filename is a String and method open is not defined in String
Use File#open
File.open(filename)
File.open("file")
opens a local file and returns a file object. Here File#open is a method of File class.
open("file")
is actually Kernel#open and looks at the string to decide what to do with it.
Trivializing things:
File.open("file") is telling Ruby specifically to open a file.
In case of open("file") Ruby examines the string "file" to determine what type it is (here a file) and open corresponding type else throws appropriate error.
Ruby has a class for dealing with paths in an object-oriented way: Pathname
require 'pathname'
loop do
print 'Enter filename: '
pn = Pathname(gets.chomp)
if pn.file?
puts "Here's your file '#{pn}':", pn.read
elsif pn.exist?
puts 'That is not a file.'
else
puts 'File does not exist.'
end
end

Printing lines from a file in ruby

When I start printing lines from a file, I get this error
#<File:0x007ff65ee297b0>
Here is the code
require 'rubygems'
File.open("sample.txt", 'r') do |f|
puts f
end
You are printing the file object. To get the contents line by line, you can use File.foreach
File.foreach('sample.txt', 'r') do |line|
puts line # called for every line
end
To process the whole file at once, you can use the read method on the file object:
File.open('sample.txt', 'r') do |file|
puts file.read # called only once
end
This is not an error. It prints correctly one line which is your File object.
Here your create a file object and you did not ask it to fetch lines or anything else for that matter.
Several good answers already. But here is another way to do it with minimal change to your code:
File.open("sample.txt", 'r').each_line do |f|
puts f
end
Another way :
IO.foreach("sample.txt") {|line| line }
Or
File.foreach('sample.txt') {|line| line }
File::open returns file handle (which apparently is being printed out as #<File:0x007ff65ee297b0>.) If you need the file content line by line you might want to use IO::readlines:
IO.readlines("sample.txt").each do |line|
puts line
end

Add a header to a CSV file with Ruby

I have a CSV file that currently does not have any headers. I would like to add a header for the first column. Is there a way to accomplish this in Ruby?
For instance, lets say my CSV looks like this at first:
1,Testing,Testing,New York
2,Testing,Testing,Boston
I want to add a header to the first column so that my csv looks like this:
my_id
1,Testing,Testing,New York
2,Testing,Testing,Boston
Here is one way to do it.
The r+ mode to file.open opens for read + write.
file = File.open("data.csv", "r+")
buffer = file.read
file.rewind
file.puts "this is my header"
file.print buffer
file.close
One possible solution...
$-i = ".orig"
if ARGV.length == 1 || ARGV.length == 2
header = ARGV.shift
buffer = ARGF.read
puts header
print buffer
else
STDERR.puts "Must supply one or two command-line arguments:"
STDERR.puts "\tdesired header (in quotes if it contains spaces)"
STDERR.puts "\tthe name of the file to prepend the header to"
STDERR.puts "If the second argument is omitted, reads from STDIN"
end
The $-i will make a backup copy of the original input file with the suffix ".orig" appended, and the original file name will now contain the specified header followed by the original contents.

Ruby: How to replace text in a file?

The following code is a line in an xml file:
<appId>455360226</appId>
How can I replace the number between the 2 tags with another number using ruby?
There is no possibility to modify a file content in one step (at least none I know, when the file size would change).
You have to read the file and store the modified text in another file.
replace="100"
infile = "xmlfile_in"
outfile = "xmlfile_out"
File.open(outfile, 'w') do |out|
out << File.open(infile).read.gsub(/<appId>\d+<\/appId>/, "<appId>#{replace}</appId>")
end
Or you read the file content to memory and afterwords you overwrite the file with the modified content:
replace="100"
filename = "xmlfile_in"
outdata = File.read(filename).gsub(/<appId>\d+<\/appId>/, "<appId>#{replace}</appId>")
File.open(filename, 'w') do |out|
out << outdata
end
(Hope it works, the code is not tested)
You can do it in one line like this:
IO.write(filepath, File.open(filepath) {|f| f.read.gsub(//<appId>\d+<\/appId>/, "<appId>42</appId>"/)})
IO.write truncates the given file by default, so if you read the text first, perform the regex String.gsub and return the resulting string using File.open in block mode, it will replace the file's content in one fell swoop.
I like the way this reads, but it can be written in multiple lines too of course:
IO.write(filepath, File.open(filepath) do |f|
f.read.gsub(//<appId>\d+<\/appId>/, "<appId>42</appId>"/)
end
)
replace="100"
File.open("xmlfile").each do |line|
if line[/<appId>/ ]
line.sub!(/<appId>\d+<\/appId>/, "<appId>#{replace}</appId>")
end
puts line
end
The right way is to use an XML parsing tool, and example of which is XmlSimple.
You did tag your question with regex. If you really must do it with a regex then
s = "Blah blah <appId>455360226</appId> blah"
s.sub(/<appId>\d+<\/appId>/, "<appId>42</appId>")
is an illustration of the kind of thing you can do but shouldn't.

Resources