Programming C, C++, Java, PHP, Ruby, Turing, VB
Computer Science Canada 
Programming C, C++, Java, PHP, Ruby, Turing, VB  

Username:   Password: 
 RegisterRegister   
 [Ruby-tut] Practical example: File I/O (and odds and ends)
Index -> Programming, Ruby -> Ruby Tutorials
View previous topic Printable versionDownload TopicRate TopicSubscribe to this topicPrivate MessagesRefresh page View next topic
Author Message
wtd




PostPosted: Fri Jul 16, 2004 10:08 pm   Post subject: [Ruby-tut] Practical example: File I/O (and odds and ends)

File I/O seems to be a popular topic in other language forums here, so I thought I'd write up a quick tutorial on doing it in Ruby.

The first step will be getting a File object for the file we want to read. There are a few possible methods of doing this. Firstly, we can create a File object in the same way any other object is created, by using the File class' new method.

For these examples, we'll use the input file "infile.txt" and the output file "outfile.dat".

code:
my_file = File.new("infile.txt", "r")


The "r" indicates the mode the file is being opened in. In this case, read-only. Other possibilities are "w", "rw", and "b" indicates binary. Binary cannot be used with "rw".

Alternatively, the File class has an "open" class method.

code:
my_file = File.open("infile.txt", "r")


But this all very boring, so let's approach this with a problem in mind. We have a file that looks like:

code:
Chris | aa | com | 3 | 9
Bob | bb | net | 1 | 16
Joe | ccccc | edu | 5 | 27
Chris | wooble | org | 6 | 3


The first column very simply contains a customer's name. The second is the domain you're hosting for them. The third is the top level domain tha domain is in. The fourth is the number of years they're signed for, and the fifth is how much they pay per year.

The output should look like:

code:
Chris owes us {some amount} over the next {so many} years for the domains {such and such}.


Step 1: Open the input file.

code:
input_file = File.open("customer_data.txt", "r")


Step 2: Get the contents of the file, then close the input file.

code:
contents = input_file.readlines
input_file.close


Step 3: Split each line on the delimiter of "|", into at most 5 columns.

code:
contents.collect! { |line| line.split(/\s*\|\s*/, 5) }


Step 4: Create an associative array (Hash) which will hold info about each customer.

code:
customers = {}


Step 5: Iterate through the contents of the file. For each user, if they're already in the customers hash, add the domain and add its cost to their total, as well as the number of years they're signed for.

code:
contents.each do |line|
   customer_name = line[0]
   customer_domain = "#{line[1]}.#{line[2]}"
   customer_rate = line[3].to_i
   customer_duration = line[4].to_i
   customer_cost = customer_rate * customer_duration

   unless customers.has_key? customer_name
      customers[customer_name] = {"domains" => [], "cost" => 0, "duration" => 0}
   end

   customers[customer_name]["domains"] << customer_domain
   customers[customer_name]["cost"] += customer_cost
   customers[customer_name]["duration"] = [customer_duration, customers[customer_name]["duration"]].max
end


This can be simplified by using the ||= operator which only assigns if the left hand side is nil.

code:
contents.each do |line|
   customer_name = line[0]
   customer_domain = "#{line[1]}.#{line[2]}"
   customer_rate = line[3].to_i
   customer_duration = line[4].to_i
   customer_cost = customer_rate * customer_duration

   customers[customer_name] ||= {"domains" => [], "cost" => 0, "duration" => 0}

   customers[customer_name]["domains"] << customer_domain
   customers[customer_name]["cost"] += customer_cost
   customers[customer_name]["duration"] = [customer_duration, customers[customer_name]["duration"]].max
end


Step 6: Open the output file. To ensure that it is created if it doesn't exist, pass the File::CREAT constant to the open method.

code:
output_file = File.open("customer_info.dat", "w", File::CREAT)


Step 7: Iterate over the customers hash and for each customer, collect a string containing the output expected in the problem summary.

code:
output_strings = customers.collect do |customer_name, customer_info|
   cn, ci = customer_name, customer_info
   "#{cn} owes us $#{ci["cost"]} over the next #{ci["duration"]} years for the domains #{ci["domains"].join(", ")}."
end


Step 8: Output those lines to the file, then close the file.

code:
output_file.puts(output_strings)
output_file.close


One note

If a block is passed to File.open, it will accept the file as an argument, run the code inside the block, and at the end, ensure that the file is closed, freeing us from having to remember to close the file.

However, since any variables created inside the block are local to the block and die when the block finishes, it's less useful for reading the input file.

The code

code:
input_file = File.open("customer_data.txt", "r")
contents = input_file.readlines
input_file.close

contents.collect! { |line| line.split(/\s*\|\s*/, 5) }

customers = {}

contents.each do |line|
   customer_name = line[0]
   customer_domain = "#{line[1]}.#{line[2]}"
   customer_rate = line[3].to_i
   customer_duration = line[4].to_i
   customer_cost = customer_rate * customer_duration

   customers[customer_name] ||= {"domains" => [], "cost" => 0, "duration" => 0}

   customers[customer_name]["domains"] << customer_domain
   customers[customer_name]["cost"] += customer_cost
   customers[customer_name]["duration"] = [customer_duration, customers[customer_name]["duration"]].max
end

output_strings = customers.collect do |customer_name, customer_info|
   cn, ci = customer_name, customer_info
   "#{cn} owes us $#{ci["cost"]} over the next #{ci["duration"]} years for the domains #{ci["domains"].join(", ")}."
end

File.open("customer_info.dat", "w", File::CREAT) { |f| f.puts(output_strings) }
Sponsor
Sponsor
Sponsor
sponsor
wtd




PostPosted: Fri Jul 16, 2004 11:14 pm   Post subject: (No subject)

Just for the heck of it, I translated the Ruby example, as closely as possible into Python code.

code:
import re

try:
        input_file = file("customer_data.txt", "r")
except IOError, e:
        file("customer_data.txt", "w").close()
        input_file = file("customer_data.txt", "r")
       
contents = input_file.readlines()
input_file.close()

contents = [re.split(r"\s*\|\s*", line, 5) for line in contents]

customers = {}

for line in contents:
        customer_name = line[0]
        customer_domain = "%s.%s" % (line[1], line[2])
        customer_rate = int(line[3])
        customer_duration = int(line[4])
        customer_cost = customer_rate * customer_duration
        if customer_name not in customers:
                customers[customer_name] = {"domains": [], "cost": 0, "duration": 0}
        customers[customer_name]["domains"].append(customer_domain)
        customers[customer_name]["cost"] += customer_cost
        customers[customer_name]["duration"] = max(customer_duration, customers[customer_name]["duration"])
       
customer_strings = ["%s owes us $%d over the next %d years for the domains %s." % (cn, ci["cost"], ci["duration"], ", ".join(ci["domains"])) for cn, ci in customers.items()]

output_file = file("customer_info3.dat", "w")
output_file.writelines([line + "\n" for line in customer_strings])
output_file.close()
Display posts from previous:   
   Index -> Programming, Ruby -> Ruby Tutorials
View previous topic Tell A FriendPrintable versionDownload TopicRate TopicSubscribe to this topicPrivate MessagesRefresh page View next topic

Page 1 of 1  [ 2 Posts ]
Jump to:   


Style:  
Search: