Ruby CSV.open will create file if one does not exist? - ruby

I'm generating a CSV file in Ruby with data from my database. I was using CSV.open(filename, "w") do |csv|
Will that create a file with that filename if one does not exist?

That depends on the options you specify when you call CSV.open().
If you call it with a "r" (read only) mode argument, then you will get an error:
No such file or directory # rb_sysopen - your_file_name.ext
If, however, you use the "w" (write) or "wb" (write binary) option, Ruby will create the file for you.
CSV.open("my_new_file.csv", "r") --> will fail if file does not exist
CSV.open("my_new_file.csv", "w") --> will create a new file

Related

adding an array to a csv file in second column using ruby

I am trying to automate a search on Google because I have more than 1 thousand lines. I can read and automate my search from the CSV, but I cannot add the array to the file. Maybe I'm missing something?
For the test, the CSV file is made up of 1 column with no header and 3 rows.
Here is my code:
require 'watir'
require 'nokogiri'
require 'csv'
browser = Watir::Browser.new(:chrome)
browser.goto("http://www.google.com")
CSV.open('C:\Users\Market\Documents\Emailhunter_scraper\test-email.csv').map do |terms|
browser.text_field(title: "Rechercher").set terms
browser.send_keys :return
sleep(rand(10))
doc = Nokogiri::HTML.parse(browser.html)
doc.css("div.f kv _SWb").each do |item|
name = item.css('a').text
link = item.css('a')[:href]
csv << [name, link]
end
sleep(rand(10))
end
sleep(rand(10))
As shown in the documentation for CSV.open, the file mode defaults to "rb".
This means the file is being opened as read-only. Instead, you need to use:
CSV.open('path/to/file/csv', 'wb')
The full documentation for different modes can be seen here. They are:
"r" Read-only, starts at beginning of file (default mode).
"r+" Read-write, starts at beginning of file.
"w" Write-only, truncates existing file
to zero length or creates a new file for writing.
"w+" Read-write, truncates existing file to zero length
or creates a new file for reading and writing.
"a" Write-only, each write call appends data at end of file.
Creates a new file for writing if file does not exist.
"a+" Read-write, each write call appends data at end of file.
Creates a new file for reading and writing if file does
not exist.
"b" Binary file mode
Suppresses EOL <-> CRLF conversion on Windows. And
sets external encoding to ASCII-8BIT unless explicitly
specified.
"t" Text file mode

in Ruby open IO object and pass each line to another object

I need to download a large zipped file, unzip it and modify each string before I save them to array.
I prefer to read downloaded zipped file line(entry) at a time, and manipulate each line(entry) as they load, rather then load the whole file in the memory.
I experimented with many IO methods of opening a file this way, but I struggle to pass a line(entry) to Zip::InputStream object. This is what I have:
require 'tempfile'
require 'zip'
require 'open-uri'
f = open(FILE_URL) #FILE_URL contains download path to .zip file
Zip::InputStream.open(f) do |io| #io is a String
while (io.get_next_entry)
io.each do |line|
# manipulate the line and push it to an array
end
end
end
if I use open(FILE_URL).each do |zip_entry|, I cannot figure out how to pass zip_entry to Zip::InputStream. Simply Zip::InputStream.open(zip_entry) does not work...
is this scenario possible, or do I have to have content of zipped file downloaded in to Tempfile completely? Any pointers so solve will be helpful

Ruby: Why can't I create a new file?

I'm trying to create a json file and write to it.
My code looks like this:
def save_as_json(object)
f = File.new('file.json')
f.puts(object.to_json, 'w')
f.close
end
save_as_json({'name'=>'fred'})
The problem is, I get the following error when I run it:
:15:in `initialize': No such file or directory # rb_sysopen - file.json (Errno::ENOENT)
I'm asking Ruby to create the file but it's complaining that it doesn't exist! What is the correct way to create and write to a file?
You just need to open the file using the 'w' mode like this:
f = File.new('file.json', 'w')
You want to determine the mode based on what you plan to do with the file, but here are your options:
"r" Read-only, starts at beginning of file (default mode).
"r+" Read-write, starts at beginning of file.
"w" Write-only, truncates existing file
to zero length or creates a new file for writing.
"w+" Read-write, truncates existing file to zero length
or creates a new file for reading and writing.
"a" Write-only, each write call appends data at end of file.
Creates a new file for writing if file does not exist.
"a+" Read-write, each write call appends data at end of file.
Creates a new file for reading and writing if file does
not exist.
IO Docs
File creation defaults to read mode, so trying to use a filespec that does not exist will result in an error:
2.3.0 :001 > f = File.new 'foo'
Errno::ENOENT: No such file or directory # rb_sysopen - foo
You need to specify 'w':
2.3.0 :002 > f = File.new 'foo', 'w'
=> #<File:foo>
That said, there are easier ways to write to files than to get a file handle using File.new or File.open. The simplest way in Ruby is to call File.write:
File.write('file.json', object.to_json)
You can use the longer File.open approach if you want; if you do, the simplest approach is to pass a block to File.open:
File.open('file.json', 'w') { |f| f << object.to_json }
This eliminates the need for you to explicitly close the file; File.open, when passed a block, closes the file for you after the block has finished executing.

Ruby: Reading from a file written to by the system process

I'm trying to open a tmpfile in the system $EDITOR, write to it, and then read in the output. I can get it to work, but I am wondering why calling file.read returns an empty string (when the file does have content)
Basically I'd like to know the correct way of reading the file once it has been written to.
require 'tempfile'
file = Tempfile.new("note")
system("$EDITOR #{file.path}")
file.rewind
puts file.read # this puts out an empty string "" .. why?
puts IO.read(file.path) # this puts out the contents of the file
Yes, I will be running this in an ensure block to nuke the file once used ;)
I was running this on ruby 2.2.2 and using vim.
Make sure you are calling open on the file object before attempting to read it in:
require 'tempfile'
file = Tempfile.new("note")
system("$EDITOR #{file.path}")
file.open
puts file.read
file.close
file.unlink
This will also let you avoid calling rewind on the file, since your process hasn't written any bytes to it at the time you open it.
I believe IO.read will always open the file for you, which is why it worked in that case. Whereas calling .read on an IO-like object does not always open the file for you.

Changing information in a CSV file

I'm trying to write a ruby script that will read through a CSV file and prepend information to certain cells (for instance adding a path to a file). I am able to open and mutate the text just fine, but am having issues writing back to the CSV without overriding everything. This is a sample of what I have so far:
CSV.foreach(path) { |row|
text = row[0].to_s
new_text = "test:#{text}"
}
I would like to add something within that block that would then write new_textback to the same reference cell(row) in the file. The only way I have to found to write to a file is
CSV.open(path, "wb") { |row|
row << new_text
}
But I think that is bad practice since you are reopening the file within the file block already. Is there a better way I could do this?
EX: I have a CSV file that looks something like:
file,destination
test.txt,A101
and need it to be:
file,destination
path/test.txt,id:A101
Hope that makes sense. Thanks in advance!
Depending on the size if the file, you might consider loading the contents of the file into a local variable and then manipulating that, overwriting the original file.
lines = CSV.read(path)
File.open(path, "wb") do |file|
lines.each do |line|
text = line[0].to_s
line[0] = "test:#{text}" # Replace this with your editing logic
file.write CSV.generate_line(line)
end
end
Alternately, if the file is big, you could write each modified line to a new file along the way and then replace the old file with the new one at the end.
Given that you don't appear to be doing anything that draws on CSV capabilities, I'd recommend using Ruby's "in-place" option variable $-i.
Some of the stats software I use wants just the data, and can't deal with a header line. Here's a script I wrote a while back to (appear to) strip the first line out of one or more data files specified on the command-line.
#! /usr/bin/env ruby -w
#
# User supplies the name of one or more files to be "stripped"
# on the command-line.
#
# This script ignores the first line of each file.
# Subsequent lines of the file are copied to the new version.
#
# The operation saves each original input file with a suffix of
# ".orig" and then operates in-place on the specified files.
$-i = ".orig" # specify backup suffix
oldfilename = ""
ARGF.each do |line|
if ARGF.filename == oldfilename # If it's an old file
puts line # copy lines through.
else # If it's a new file remember it
oldfilename = ARGF.filename # but don't copy the first line.
end
end
Obviously you'd want to change the puts line pass-through to whatever edit operations you want to perform.
I like this solution because even if you screw it up, you've preserved your original file as its original name with .orig (or whatever suffix you choose) appended.

Resources