Back-end Engineering Articles

I write and talk about backend stuff like Ruby, Ruby On Rails, Databases, Testing, Architecture / Infrastructure / System Design, Cloud, DevOps, Backgroud Jobs, and more...

Twitter:
@daniel_moralesp

2019-08-09

How does Ruby File Management Works?

Sometimes we'll need to deal with file management with Ruby. For instance, you receive a .txt file from your Project Manager (PM), and you have to read what's inside. Other times you have to write something there. Cases like this can happen with different types of files, like .csv, .pdf, .xls, or .txt among others. 

This is a common task in Ruby, and you have to deal with them accordingly. Previously, we talked about Ruby Gems; plenty of them help us deal with all these kinds of file formats. Later we'll be studying those formats in detail, but for now, we'll be seeing just .txt file formats and using the built-in "File" Ruby class. Before that, we have to understand what's I/O.

I/O

In computing, input/output (I/O, or informally io or IO) is the communication between an information processing system, such as a computer, and the outside world, possibly a human or another information processing system. Inputs are the signals or data received by the system, and outputs are the signals or data sent from it. The term can also be used as part of an action; "perform I/O" is to perform an input or output operation. For more info about I/O in ruby, please go ahead here.

Ruby provides a whole set of I/O-related methods implemented in the Kernel module. All the I/O methods are derived from the class IO. The class IO offers all the basic methods, such as read, write, gets, puts, readline, getc, and printf.

So far, we've seen a couple of them, like the "puts" command print something on the console or the "gets" command to receive user inputs. Next, we will see the "read" and "write" commands. But after that, let's check the class "File" in Ruby.

A "File" is an abstraction of any file object accessible by the program and is closely associated with class IO. So, let's check how it works.

TXT Files (text files)

A text file is a kind of computer file structured as a sequence of lines of text. A text file exists stored as data within a computer file system. 

On modern operating systems such as Microsoft Windows and Unix-like systems, text files do not contain any special EOF (end of the line) character because file systems on those operating systems keep track of the file size in bytes. Therefore, most text files need to have end-of-line delimiters, which are done in a few different ways depending on the operating system. For example, some operating systems with record-oriented file systems may not use newline delimiters and will primarily store text files with lines separated as fixed or variable length records.

So, as you can see, there is a lot of attention under the delimiters. Basically, if you end a line of text, it will be followed by an "enter" or a "line space," which will be the End of the Line. And that's how text files store data (line by line) and is how we can create, read, edit or delete such textfile.
 


Read

First, we need to create a new text file under our folders that will contain the .txt File. So let's begin.

➜  blog_tutorials mkdir file_management
➜  blog_tutorials touch file_management/content.txt 
➜  blog_tutorials cd file_management 
➜  file_management ls
content.txt


Now let's open it from our Text Editor and start wiring something inside the text file. But, first, let's do something straightforward:

content.txt

my first line
my second line
my third line



With the File created, we're ready to use the Read method from Ruby. To do this, we have to know the File class in ruby. The File class in Ruby is an abstraction of any file object accessible by the program and is closely associated with class IO. But here, you're probably confused because you could ask what the difference between I/O Ruby Class and File Ruby Class is? An excellent question to ask, and here is the answer: since File is a subclass of IO and it does not have the read method when you invoke File.read, you are actually calling IO.read no difference here.

Now we can go ahead and call the ".read" method. Go to your console, access the folder "file_management," which we created before with the command mkdir (where we have the file content.txt), and open the IRB Ruby console.


➜  file_management irb
2.6.8 :001 > File.read("content.txt")
 => "my first line\nmy second line\nmy third line" 
2.6.8 :002 > 




We've invoked the Read method from I/O through the File class in Ruby. However, the output seems a bit weird, and it's because of the End of the Line. We have line breaks in your original document, so ruby reads and prints the result by adding \ instead of EOLs. If we want to get the same output as the original, we can follow 3 basic steps:

  • * Step #1- Assign the Read output to a variable
  • * Step #2- Split the output by the line break. This split converts the string into an array with each different line of text.
  • * Step #3- iterate over the array, which is basically each line of text we split before



2.6.8 :007 > content = File.read("content.txt")
 => "my first line\nmy second line\nmy third line" 
2.6.8 :008 > lines = content.split("\n")
 => ["my first line", "my second line", "my third line"] 
2.6.8 :009 > lines.each do |line|
2.6.8 :010 >     puts line
2.6.8 :011?>   end
my first line
my second line
my third line
 => ["my first line", "my second line", "my third line"] 


As you can see, the "split" method is the key here because we choose the pattern "\n" as the rule to separate each line of text and save the result as an array of different sequences of text. 

Write

Now let's use the" .write" method. This method doesn't need to have a file previously created. It does for us to check if the File exists; if so, they overwrite the File with the content we pass through the method. If the File doesn't exist, it creates it and adds the content. So let's do it.

2.6.8 :012 > File.write("content2.txt", "Once upon a time..")
 => 18 



And now, let's check our text editor.


We have the new File created and the text inside it. Awesome! But what happens if we try to add different content with the same command? 


It will overwrite the current one. BTW the 18 number refers to the number of characters we insert in the File


And that can become a problem for us. So here is where the "Open" method comes in help. 

The "Open" method allows us to be more organized and follow similar behavior as we write directly in the files as human beings. We have to first open the File, then keep it open until we write, and then close the File when we finish. If we need to add more content, we follow the same steps. So let's do it but from our console.

2.6.8 :025 > File.open("content2.txt", "a+") do |file|
2.6.8 :026 >     file.write("\nNow we add new line...")
2.6.8 :027?>   end
 => 23 


Let's check what we did here:

  • * In the first line of code, we've called the" .open" method and passed 2 arguments; the first argument is the File's name. If it exists, we use it; if not, we create a new one. The second argument is so important. We can pass different argument names here, depending on the task we want to do. For now, we're using a+ who adds the content at the end of the File. In the same line, we open a ruby block and create a variable named "file," which will contain the File we opened. 

  • * We called the ".read" method inside the ruby block with the content as the argument. We started the text of the argument with the symbol "\n" which represents a new line break. After that, and without any space, we add the text.

  • * Finally, we close the ruby block.

Let's check the result.


We added new text without overwriting the current one. That's awesome!

Opening a File using Different Modes in Ruby

This is technically called I/O Open Modes. This is because Ruby lets you open a file with different permissions (modes). For example, you can open a file in read-only mode, write-only mode, or read-write mode. Here you have a description of each one of them.


We have these 6 basic open modes. We've used "a+" to append data at the end of the File. And with the help of the character "\n" to do the breaking. 

As always, there are a lot of methods to use here with File class or IO class in ruby, and here you can find more details about them:

IO Class


File Class



Are these used in real-life scenarios?



Probably you're asking yourself if these kinds of features are requested in real-life projects. The answer is it depends. Some solutions will need that you deal with text files. Some others don't. The reality is that you won't use this too much in your day-to-day job, but it is an excellent idea to know the basics about it. For that reason, we're not going deeper here (probably in the future blog post), but if you're required to work with file texts, you know that Ruby can deal with it efficiently. 

For now, I hope you have learned a lot.

Thanks for reading!
Daniel Morales