52

When is it considered proper to use Ruby's StringIO as opposed to just using String?

I think I understand the fundamental difference between them as highlighted by "What is ruby's StringIO class really?", that StringIO enables one to read and write from/to a String in a stream-oriented manner. But what does this mean practically?

What is a good example of a practical use for using StringIO when simply using String wouldn't really cut it?

Community
  • 1
  • 1
Scott Joseph
  • 523
  • 1
  • 4
  • 4

2 Answers2

77

Basically, it makes a string look like an IO object, hence the name StringIO.

The StringIO class has read and write methods, so it can be passed to parts of your code that were designed to read and write from files or sockets. It's nice if you have a string and you want it to look like a file for the purposes of testing your file code.

def foo_writer(file)
  file.write "foo"
end

def test_foo_writer
  s = StringIO.new
  foo_writer(s)
  raise 'fail' unless s.string == 'foo'
end
BinaryButterfly
  • 18,137
  • 13
  • 50
  • 91
David Grayson
  • 84,103
  • 24
  • 152
  • 189
  • 3
    Interesting. Are there any advantages (with regard to memory usage) in using StringIO as opposed to String? – Scott Joseph Sep 26 '12 at 00:07
  • 3
    Probably not. If you look at the source code it looks like it just keeps a reference to a String object internally, so it will use slightly more memory than a normal string: https://github.com/ruby/ruby/blob/trunk/ext/stringio/stringio.c – David Grayson Sep 26 '12 at 00:50
  • 3
    While there isn't a memory advantage if you are using `StringIO`, if you design your functions for IO objects you can do things such as read from a file, and otherwise pipe operations together. So a function that takes an IO object can use less memory if it is used with an appropriate class. So, this lets you operate on a String or a possibly more efficient IO object in the same way. – Kevin Cox Aug 10 '13 at 03:05
  • Keep in mind a `StringIO` doesn't look _exactly_ like a `File`. One "gotcha" i just ran into -- `File.size(StringIO.new('foo')) #=> TypeError: no implicit conversion of StringIO into String` – mziwisky Oct 18 '16 at 22:57
30

I really like StringIO for the use-case of appending text line-by-line without having to use "\n" over and over again. For example, instead of this:

s = ''
s << "\n" << "some text on a new line"
s << "\nthis is pretty awkward"
s = "#{s}\neven more ugly!"

I can do this

s = StringIO.open do |s|
  s.puts 'adding newlines with puts is easy...'
  s.puts 'and simple'
  s.string
end

Which is much cleaner. It isn't necessary to use the block form of String.IO, you can create an object like so: s = StringIO.new but regardless, make sure to keep in mind the actual string is accessed via the StringIO#string method.

Steve Benner
  • 1,679
  • 22
  • 26
  • 1
    I usually use `Array#<<` and then `Array#join` for this kind of thing, though using `StringIO` is a great technique. – jc00ke Nov 25 '14 at 23:30
  • 5
    @jc00ke So did I actually. Now that I'm more familiar with Ruby though, my habits result in code favoring the **reader**... To illsutrate what I mean, consider the following: To the coder, using `Array` like you describe is convenient to *write* in, sure, but when *reading* code, the class name `StringIO` is a **far** better indicator of purpose. Not to mention `Array` is a fundamentally different data structure. Also, the neccessity of using `Array#join` incurs excess code 'baggage', and worse, complexity! For instance, it's argument is set by default to a global which is subject to change. – Steve Benner Dec 17 '14 at 11:33
  • I love this, I'm stealing it. Right now. – ian Mar 22 '18 at 12:59