Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why doesn't Ruby have a real StringBuffer or StringIO?

I looked at the ruby documentation for StringIO, and it looks like what you want is StringIO#string, not StringIO#to_s

Thus, change your code to:

s = StringIO.new
s << 'foo'
s << 'bar'
s.string

Like other IO-type objects in Ruby, when you write to an IO, the character pointer advances.

>> s = StringIO.new
=> #<StringIO:0x3659d4>
>> s << 'foo'
=> #<StringIO:0x3659d4>
>> s << 'bar'
=> #<StringIO:0x3659d4>
>> s.pos
=> 6
>> s.rewind
=> 0
>> s.read
=> "foobar"

I did some benchmarks and the fastest approach is using the String#<< method. Using StringIO is a little bit slower.

s = ""; Benchmark.measure{5000000.times{s << "some string"}}
=>   3.620000   0.100000   3.720000 (  3.970463)

>> s = StringIO.new; Benchmark.measure{5000000.times{s << "some string"}}
=>   4.730000   0.120000   4.850000 (  5.329215)

Concatenating strings using the String#+ method is the slowest approach by many orders of magnitude:

s = ""; Benchmark.measure{10000.times{s = s + "some string"}}
=>   0.700000   0.560000   1.260000 (  1.420272)

s = ""; Benchmark.measure{10000.times{s << "some string"}}
=>   0.000000   0.000000   0.000000 (  0.005639)

So I think the right answer is that the equivalent to Java's StringBuffer is simply using String#<< in Ruby.


Your example works in Ruby - I just tried it.

irb(main):001:0> require 'stringio'
=> true
irb(main):002:0> s = StringIO.new
=> #<StringIO:0x2ced9a0>
irb(main):003:0> s << 'foo'
=> #<StringIO:0x2ced9a0>
irb(main):004:0> s << 'bar'
=> #<StringIO:0x2ced9a0>
irb(main):005:0> s.string
=> "foobar"

Unless I'm missing the reason you're using to_s - that just outputs the object id.


Well, a StringBuffer is not quite as necessary in Ruby, mainly because Strings in Ruby are mutable... thus you can build up a string by modifying the existing string instead of constructing new strings with each concat.

As a note, you can also use special string syntax where you can build a string which references other variables within the string, which makes for very readable string construction. Consider:

first = "Mike"
last = "Stone"
name = "#{first} #{last}"

These strings can also contain expressions, not just variables... such as:

str = "The count will be: #{count + 1}"
count = count + 1