Computer Science Programming Basics in Ruby (2013)
Chapter 11. File Input/Output
IN THIS CHAPTER
§ Reading files
§ Writing files
Until this point, all of our programs have taken a data set of values from user input. Any given program took in one data set, which consisted of one or more related values from the user, and then performed a computation based on that data set. Imagine a program that has many data sets, each consisting of multiple values. Entering all the data sets by hand could be somewhat time-consuming. Also, some data sets may come from external sources and not directly from the user.
The techniques presented in this chapter will allow you to manipulate files and utilize their data within your software. File input/output (I/O) allows software to perform computations on sets of data until a desired end result is achieved.
11.2 File Access: Reading and Writing
To access files, the built-in Ruby File class is used. This class contains multiple methods. We use the following: open, close, gets, and puts. The method File.open instantiates a new object that enables Ruby to read from or write to an existing or new file. The object returned by File.open can then be used by Ruby to access the file. The following code shows how to open a file:
myfile = File.open(file_name, access_mode)
The myfile variable is a File object that can now be used to interact with the file’s contents, depending on what access mode is used. Note that file name and access mode are strings. The variable file_name is a representation of a path to a file, such as /home/ruby/file.txt. The myfile variable in our program is local to only our program. Two of the most basic access modes and what they do are shown in Table 11-1.
Table 11-1. Some file access modes
Read access only. Points to start of the file. This is the default, but it is good programming style to specify it anyway.
Write access only. Points to the beginning of the file that will overwrite the file’s content if it already exists.
GEM OF WISDOM
Files provide access to data resident in the computer system’s long-term memory. Storage in long-term memory, such as disks, provides data resiliency, namely, permanence. That is, disks work even without power, so they store things far longer than the internal random access memory, which works only if the power is on.
To read a line of characters from a file, call the gets method on the myfile object. When the gets method is called, it reads characters until it reaches a newline character (\n), and then it returns what it read. The File object keeps track of what has been read in, so each successive call to gets will always return the next line, until it reaches the end of the file and returns nil. nil has the logical truth value of false. Every other value returned by getshas the logical value of true. Consider the code provided in Example 11-1, which reads in a file foo.txt and then prints it out.
Example 11-1. Sample code for file reading
1 myfile = File.open("foo.txt", "r")
2 whole_file = ""
4 while (input_line = myfile.gets)
5 whole_file += input_line
8 puts "Contents of input file:"
9 puts whole_file
§ Line 1 opens foo.txt for read access.
§ Line 2 instantiates an empty string variable, whole_file, which will be used to store the file to display.
§ Lines 4–6 contain a loop that reads the file into input_line, one line at a time, and appends each line into whole_file.
§ Line 9 prints out the file.
Sequentially writing to a file is similar to sequentially reading from a file. Instead of opening the file in read mode, the file needs to be opened with w for writing access. To write text to a file, use the local variable myfile to call the puts method:
myfile.puts("text goes here!")
The code shown in Example 11-2 opens a file for write access, accepts user input, and writes that input to the file. It will then close the file, reopen it, and print the contents to verify correctness.
Example 11-2. File read/write example
1 file_a = File.open("bar.txt", "w")
3 puts "Please enter a line of text"
4 line = gets()
8 file_b = File.open("bar.txt", "r")
9 puts "Contents of file:"
10 puts file_b.gets()
§ Line 1 opens bar.txt as file object file_a with write access.
§ Lines 3–4 take text input from the user.
§ Line 5 writes the user input into "bar.txt".
§ Line 6 closes the File object file_a (this saves the text inside it by closing the file and preventing further access to it).
§ Line 8 instantiates a new File object file_b. This object will be used for the purpose of reading the content of bar.txt. Note the file access mode r.
§ Line 10 outputs the contents of the newly created file to the console, illustrating that the code in Example 11-2 behaves in the desired fashion.
11.2.1 File Reader Class
We will now define two classes that encapsulate reading in a file and writing out a file. Then we will use the two classes to create a file copy program that will read text from a file and then copy the text into a new file.
The class presented in Example 11-3 encapsulates reading and displaying a file.
Example 11-3. FileReader class
1 class FileReader
3 def initialize(file_name)
4 @file = File.open(file_name, "r")
7 def read_file
8 whole_file = ""
9 while (input_line = @file.gets)
10 whole_file += input_line
13 return whole_file
16 def display
17 puts "Contents of input file:"
18 puts read_file
21 def close
§ Lines 3–5 define the constructor. The variable @file references the File object that has been opened.
§ Line 4 opens the file for the purpose of reading the contents. The r indicates that this file is to be open for reading.
§ Lines 7–14 define the read file method. This method incorporates a basic loop that goes through a given file using gets for the purpose of reading one line at a time.
§ Line 10 appends (adds to the end of the string) to whole_file the contents of the currently read line of the file.
§ Line 13 returns the contents of the file that is now stored in whole_file.
§ Lines 16–19 define a display method that outputs the contents of the file.
§ Lines 21–23 define a close method that closes the opened file, @file.
11.2.2 FileWriter Class
The class in Example 11-4 encapsulates writing to and closing a file.
Example 11-4. FileWriter class
1 class FileWriter
3 def initialize(file_name)
4 @file = File.open(file_name, "w")
7 def write_line(output_line)
11 def close
§ Lines 3–5 define the constructor.
§ Line 4 opens the file file_name using w for write access mode.
§ Lines 7–9 define the write_line method, which outputs a single line, output_line, to a file associated with the FileWriter object.
§ Line 8 makes use of the puts method to output the contents of a given output_line to the file referenced by @file.
§ Lines 11–13 close the file. This ensures that all the data that have been written to the file are actually written to it. That is, data that are temporarily buffered in intermediate storage are actually written to secondary (permanent) storage. Buffering is commonly used to reduce output writing delays.
The above use of buffers may seem obscure for a reader who is not familiar with the actual mechanisms employed in writing a file. In reality, the puts method does not actually write to a file; it actually fills a holding area commonly referred to as a buffer.
11.2.3 File Reader/Writer Example
The code in Example 11-5 will read in a file and write the contents out to a new file. This code assumes you have written out the class definitions for FileReader and FileWriter given in the preceding section as files named file_reader.rb and file_writer.rb.
Example 11-5. FileReader/FileWriter example
1 require_relative "file_reader.rb"
2 require_relative "file_writer.rb"
4 fr = FileReader.new("input.txt")
5 fw = FileWriter.new("output.txt")
7 input = fr.read_file()
§ Lines 1 and 2 import the class definitions of FileReader and FileWriter.
§ Lines 4 and 5 create instances of our two classes, the first for input, the second for output.
§ Line 7 reads the file into a string called input.
§ Line 8 writes the string input out to the file held by the instance of FileWriter.
§ Lines 10 and 11 close both of the open files.
To verify that the code presented in Example 11-5 works correctly, use the input text file with the content shown in Example 11-6. After running Example 11-5’s code with the input from Example 11-6, ensure that output.txt is the same as input.txt.
Example 11-6. Sample input file
A mighty fine day for ruby programming!
Computer Science is the best!
We described basic file input and output operations.
11.3.1 Key Concepts
§ Text files in Ruby are contiguous streams of data in the form of characters.
§ Data can be entered into a program using file access; this is done through file input/output or I/O.
§ To read from or write to a file, one must access the file through Ruby’s File class.
11.3.2 Key Definitions
§ File access: Opening a file for reading or writing.
§ Input: Reading information from a stored file.
§ Output: Writing information to a file for storage.
1. The game of Go is often played online. Many users save their game data for later analysis. The game is played by two players who take turns placing stones (one player using black stones and the other using white stones) on the intersections of a 19 × 19 board. Create a program to enter each move of a game and save that information to a file.
2. Write a program that quizzes the user on her or his vocabulary. Make the program read a set of words and definitions from a file; display definitions one at a time, in random order; and prompt for the appropriate word.
3. Write a program that will keep track of the mileage for oil changes for your car.
4. Write a program that reads the content of a file and outputs it to the screen.
5. Write a program that takes input from the user and saves it to a file.
6. Write a program that creates and stores a simple address book.