Почему в Ruby нет реального StringBuffer или StringIO?

Я недавно прочитал хороший пост по использованию StringIOв Ruby. Однако автор не упоминает, что StringIOэто просто «я». Там нет "О" Вы не можете сделать это, например:

s = StringIO.new
s << 'foo'
s << 'bar'
s.to_s
# => should be "foo\nbar"
# => really is ''`

Ruby действительно нужен StringBuffer, как и в Java. StringBuffers служат двум важным целям. Во-первых, они позволяют вам проверить выходную часть того, что делает Ruby's StringIO. Во-вторых, они полезны для создания длинных струн из мелких деталей - то, что Джоэл напоминает нам снова и снова, в остальном очень и очень медленно.

Есть хорошая замена?

Это правда, что строки в Ruby являются изменчивыми, но это не значит, что мы всегда должны полагаться на эту функциональность. Если stuffон большой, то требования к производительности и памяти, например, очень плохие.

result = stuff.map(&:to_s).join(' ')

«Правильный» способ сделать это в Java:

result = StringBuffer.new("")
for(String s : stuff) {
  result.append(s);
}

Хотя моя Java немного ржавая.

13.08.2008 20:19:09
"Мега Горничная?" Никогда о ней не слышал. Я никогда не верил в StringBuffers, но я всегда использовал их из-за страха, что кто-то увидит мой код. Но на самом деле, такие вещи когда-нибудь складываются?
Dan Rosenstark 6.05.2009 01:53:03
Вероятно, ссылка «SpaceBalls».
Stephen Eilert 30.08.2010 16:59:35
Мега горничная была удалена как побочный ущерб от избавления от ненормативной лексики.
Andrew Grimm 9.05.2011 03:52:29
Ваш пример объединения строк не эквивалентен коду Java. Как вы уже, Рубиновые строки изменчивы, поэтому в Ruby , вы просто сделать: stuff.inject('') { |res, s| res << s.to_s }. Вы можете смело полагаться на то, что строки Ruby являются изменяемыми, это не изменится, так как это разрушит все существующие приложения Ruby.
Theo 9.05.2011 05:01:29
Я действительно не понимаю, почему StringIO не имеет метода to_s. Это класс, который управляет строкой, поэтому, если вы хотите эту строку, вы должны специально запросить ее. Он должен иметь метод to_s, так как это соглашение ruby, но это не так. (Кто-то может исправить меня, если я ошибаюсь)
hcarreras 16.07.2014 16:10:59
5 ОТВЕТОВ
РЕШЕНИЕ

Я посмотрел документацию по ruby StringIO, и похоже, что вы хотите StringIO#string, а неStringIO#to_s

Таким образом, измените свой код на:

s = StringIO.new
s << 'foo'
s << 'bar'
s.string
114
1.10.2013 09:02:17

Что ж, StringBuffer не совсем необходим в Ruby, главным образом потому, что строки в Ruby являются изменяемыми ... таким образом, вы можете создать строку, изменив существующую строку вместо создания новых строк с каждым concat.

Как примечание, вы также можете использовать специальный синтаксис строки, где вы можете создать строку, которая ссылается на другие переменные в строке, что делает конструкцию очень читаемой строки. Рассматривать:

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

Эти строки также могут содержать выражения, а не только переменные ... такие как:

str = "The count will be: #{count + 1}"
count = count + 1
3
13.08.2008 20:31:29
Это, безусловно, правда, и это отлично подходит для коротких интерполяций. Хотя это плохо для создания длинных строк, таких как HTML-страницы. См. En.wikipedia.org/wiki/Schlemiel_the_painter%27s_Algorithm
James A. Rosen 9.06.2009 12:15:13
Конечно, но для построения HTML-страниц, почему бы не использовать что-то, что построено для этой функции, например HAML или ERB?
Earl Jenkins 2.11.2011 22:23:19
re: «Алгоритм Шлемеля-живописца» Если вы используете StringIO, как указано выше, критика Джоэла Спольски неприменима. StringIO имеет указатель поиска, как файл. Нет необходимости каждый раз пересчитывать конец строки. А для более длинных строк вы можете использовать% {} или библиотеку, подобную той, которую предложил Эрл.
CJ. 4.12.2013 05:38:04

Ваш пример работает в 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 - это просто выводит идентификатор объекта.

12
13.08.2008 20:46:30

Как и другие объекты типа IO в Ruby, при записи в IO указатель символа перемещается вперед.

>> s = StringIO.new
=> #<StringIO:0x3659d4>
>> s << 'foo'
=> #<StringIO:0x3659d4>
>> s << 'bar'
=> #<StringIO:0x3659d4>
>> s.pos
=> 6
>> s.rewind
=> 0
>> s.read
=> "foobar"
34
19.04.2019 16:08:21
Мне это на самом деле не нужно, так как есть StringIO#read, но я всегда фанат знания нескольких способов сделать что-то. +1
James A. Rosen 9.08.2009 20:36:01
Гм, я имел в видуStringIO#string
James A. Rosen 9.08.2009 20:36:33

Я сделал несколько тестов, и самый быстрый подход - использование 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 StringBufferпросто используется String#<<в Ruby.

24
27.09.2018 04:15:19
Какая версия ruby ​​использовалась для этого теста, пожалуйста?
Jared Beck 17.03.2014 03:03:40
Вау. Так что это должен быть правильный ответ, так как String самый быстрый. Проверено на ruby ​​2.1.5 и те же результаты.
Nikkolasg 17.12.2014 18:05:58
и что происходит, когда ruby ​​делает неизменяемыми строки? Такие микрооптимизации являются адом в конце.
akostadinov 15.12.2016 16:39:49
Что произойдет, если вы захотите добавить несколько символов в очень длинную строку? Я думаю, что StringIOтогда будет быстрее
nothing-special-here 12.02.2017 22:01:19
Решение для конкатенации строк не будет работать с замороженными строковыми литералами, но StringIOработает.
KARASZI István 15.03.2020 09:00:08