Недавно я прочитал хороший пост об использовании StringIO
в Ruby. Однако автор не упоминает, что StringIO
- это просто "I." В нем нет "O." Вы _не можете сделать это, например:
s = StringIO.new
s << 'foo'
s << 'bar'
s.to_s
# => should be "foo\nbar"
# => really is ''`
Ruby действительно нужен StringBuffer, такой же, как в Java. Строковые буферы служат двум важным целям. Во-первых, они позволяют тестировать вывод половины того, что делает Ruby's StringIO. Во-вторых, они полезны для создания длинных строк из маленьких частей - то, о чем Джоэл напоминает нам снова и снова, иначе это очень медленно.
Есть ли хорошая замена?
Это правда, что строки в Ruby являются изменяемыми, но это не значит, что мы всегда должны полагаться на эту функциональность. Если stuff
большой, то требования к производительности и памяти этого, например, очень плохие.
result = stuff.map(&:to_s).join(' ')
"правильный" способ сделать это в Java следующий:
result = StringBuffer.new("")
for(String s : stuff) {
result.append(s);
}
Хотя мой Java немного заржавел.
Я просмотрел документацию ruby для StringIO
, и похоже, что вам нужно StringIO#string
, а не StringIO#to_s
.
Таким образом, измените свой код на:
s = StringIO.new
s << 'foo'
s << 'bar'
s.string
Как другие объекты IO-типа в Руби, когда Вы пишете IO, достижениям указателя характера.
>> s = StringIO.new
=> #<StringIO:0x3659d4>
>> s << 'foo'
=> #<StringIO:0x3659d4>
>> s << 'bar'
=> #<StringIO:0x3659d4>
>> s.pos
=> 6
>> s.rewind
=> 0
>> s.read
=> "foobar"
Я сделал некоторые оценки, и самый быстрый подход использует 'String#< <'; метод. Используя 'StringIO' немного медленнее.
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)
Связывая последовательности, используя 'String# +' метод - самый медленный подход многими порядками величины:
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)
Таким образом, я думаю, что правильный ответ то, что эквивалент Java' s 'StringBuffer' просто использует 'String#< <'; в Руби.
Ваш пример работает в Ruby - я только что попробовал.
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"
Если я не ошибаюсь, вы используете to_s - это просто выводит id объекта.
В Ruby StringBuffer не так уж необходим, главным образом потому, что строки в Ruby являются изменяемыми... таким образом, вы можете наращивать строку, изменяя существующую строку, а не создавать новые строки при каждом конкатенате.
В качестве примечания, вы также можете использовать специальный синтаксис строк, где вы можете построить строку, которая ссылается на другие переменные внутри строки, что делает построение строки очень читабельным. Рассмотрим:
first = "Mike"
last = "Stone"
name = "#{first} #{last}"
Эти строки также могут содержать выражения, а не только переменные... например:
str = "The count will be: #{count + 1}"
count = count + 1