In Progress
Unit 1, Lesson 21
In Progress

Output Record Separator

Did you know Ruby has a `print` method in addition to `puts`? Did you know it’s for record-oriented output? Do you know how to control the formatting of records in Ruby output streams? Find out all this and more in today’s video!

Video transcript & code

When I first learned Ruby, I learned that it had a print method which would do exactly what its name sounded like, and print text to standard output.

print "Hello, world."

# >> Hello, world

But I also learned that with print, if I forgot to add a newline to the end of the argument, I'd mess up my output.

print "Hello, world"
print "How are you today?"

# >> Hello, worldHow are you today?

I was pretty used to this from other programming languages. But I learned that there was this other, less obviously-named method called puts. puts would automatically add a newline to the ends of my strings.

puts "Hello, world"
puts "How are you today?"

# >> Hello, world
# >> How are you today?

In fact, it was even "smart": if I did supply a newline, it wouldn't add an extra!

puts "Hello, world\n"
puts "How are you today?"

# >> Hello, world
# >> How are you today?

The upshot of this was that I learned the same lesson every other Ruby newbie learns: to always use puts for output.

And to ignore the existence of the print method, even though it has the more obvious name for printing output.

puts "I'm everyone's favorite!"
print "Nobody likes me 😭"

# >> I'm everyone's favorite!
# >> Nobody likes me 😭

Now before we go forward, let's be precise about these two methods. So far we've been using the global print and puts methods from the Kernel module.

But these global methods are effectively shortcuts to two methods that are available on every writeable IO object. When we say puts and print, we're implicitly invoking them on the standard output object.

$stdout.puts "I'm everyone's favorite!"
$stdout.print "Nobody likes me 😭"

# >> I'm everyone's favorite!
# >> Nobody likes me 😭

And we could just as well be invoking them on a file object. The point being, these methods are not specific to writing text to the console. They are applicable to any output stream.

out = open("out.txt", "w")
# => #<File:out.txt>

out.puts "I'm everyone's favorite!"
out.print "Nobody likes me 😭"
out.close

File.read(out)
# => "I'm everyone's favorite!\n" + "Nobody likes me 😭"

I'm going to go back to using the global versions now, but bear in mind that what we're about to see is applicable to any output stream.

So anyway, the lesson I originally took away was that puts was the cool method that did what I meant, and print was the boring one that just printed exactly what it was given. But this turns out to be a misunderstanding. It's more accurate to say that puts is screen-oriented, and print is record-oriented.

To understand what this means, we need to interact with a global variable which goes by the unlikely shorthand name of $.

If we require the English module, we get to call it $OUTPUT_RECORD_SEPARATOR.

Or $ORS.

$\ # => nil
require "English"
$OUTPUT_RECORD_SEPARATOR  # => nil
$ORS  # => nil

We can see that this variable starts out as nil. Let's set it to a newline instead.

And then let's do some output with print.

$\ = "\n"
$\ # => "\n"
require "English"
$OUTPUT_RECORD_SEPARATOR  # => "\n"
$ORS  # => "\n"

print "Hello, world"
print "Let the record state..."
print "I'm in a newline of business!"

# >> Hello, world
# >> Let the record state...
# >> I'm in a newline of business!

Look at that! print is adding newlines now!

If we add a second newline to the output record separator, we get double-spaced output.

$\ = "\n\n"
$\ # => "\n\n"
require "English"
$OUTPUT_RECORD_SEPARATOR  # => "\n\n"
$ORS  # => "\n\n"

print "Hello, world"
print "Let the record state..."
print "I'm in a newline of business!"

# >> Hello, world
# >> 
# >> Let the record state...
# >> 
# >> I'm in a newline of business!
# >> 

We could even insert visual separator between each line of output.

$\ = "\n----\n"
$\ # => "\n----\n"
require "English"
$OUTPUT_RECORD_SEPARATOR  # => "\n----\n"
$ORS  # => "\n----\n"

print "Hello, world"
print "Let the record state..."
print "I'm in a newline of business!"

# >> Hello, world
# >> ----
# >> Let the record state...
# >> ----
# >> I'm in a newline of business!
# >> ----

What we can see here is that it's not as simple as saying puts adds a newline, whereas print outputs exactly the string it was given. It's more accurate to say that puts generally does what we want for human-oriented output to a screen. Whereas print outputs strings as records, with whatever record separator is currently defined.

Where this fact really becomes useful is in command-line one-liners.

For instance, let's say we have a text file that has Windows-style CRLF line endings.

$ cat -v jabberwocky.txt 
'Twas brillig, and the slithy toves^M
      Did gyre and gimble in the wabe:^M
All mimsy were the borogoves,^M
      And the mome raths outgrabe.^M
^M

We're using the -v flag to the cat command, which shows carriage returns as ^M sequences.

We can convert this file's line-endings to UNIX-style newlines by running a -n loop over the input. I've talked about -n and -p loops in other videos, but if you aren't familiar, it implicitly surrounds the code provided at the command-line with a loop reads in each line of the input in turn.

The code we evaluate at the command line sets the output record separator to a newline, chomps any line-ending characters off the current line, and then prints the current line. I'll go into greater detail on the chomp method another time.

Here we're making use of the fact that in these one-liners, print without an explicit argument automatically prints the current line.

We feed the output of this into cat -v again and see that, indeed, the carriage returns are gone.

$ ruby -ne '$\="\n"; chomp; print' jabberwocky.txt | cat -v
'Twas brillig, and the slithy toves
      Did gyre and gimble in the wabe:
All mimsy were the borogoves,
      And the mome raths outgrabe.

If we switch from -n to -p, we don't have to write out the print at all, because Ruby does it for us.

$ ruby -pe '$\="\n"; chomp' jabberwocky.txt | cat -v
'Twas brillig, and the slithy toves
      Did gyre and gimble in the wabe:
All mimsy were the borogoves,
      And the mome raths outgrabe.

Just to prove that Ruby uses print under the hood for -p loops, let's add an extra newline to our output record separator.

$ ruby -pe '$\="\n\n"; chomp' jabberwocky.txt | cat -v
'Twas brillig, and the slithy toves

      Did gyre and gimble in the wabe:

All mimsy were the borogoves,

      And the mome raths outgrabe.

OK, but even this is more than we actually need to type.

If you have seen my video on line-ending-mode, you might recall that with the -l flag, Ruby both automatically chomps the current input line, and it sets the output record separator to the newline character.

$ ruby -le 'p $\'
"\n"

Which is exactly what we're doing in our Ruby code here: setting the output record separator to newline, and chomping the current line.

So with -l we don't need any code at all to normalize the input text to linefeed endings, because by line-ending mode by definition does everything we wanted.

$ ruby -ple '' jabberwocky.txt | cat -v
'Twas brillig, and the slithy toves
      Did gyre and gimble in the wabe:
All mimsy were the borogoves,
      And the mome raths outgrabe.

So what have we learned here? The important point to take away is that Ruby has both screen-oriented and record-oriented output. And for record-oriented output, we can pick our own output separator. Happy hacking!

Responses