56 votes

Pourquoi Ruby n’a-t-il pas un véritable StringBuffer ou StringIO?

J'ai lu récemment un joli post sur l'utilisation de l' StringIO en Ruby. Ce que l'auteur ne mentionne pas, cependant, est qu' StringIO est juste un "I". Il n'y a pas de "O". Vous ne pouvez pas le faire, par exemple:

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

Ruby a vraiment besoin d'un StringBuffer comme l'a fait la Java. StringBuffers servir deux objectifs importants. Tout d'abord, ils vous permettent de tester la sortie de la moitié de ce que Ruby StringIO n'. Deuxièmement, ils sont utiles pour la construction de longues chaînes de petites pièces -- quelque chose que Joel nous rappelle encore et encore est par ailleurs très très lent.

Est-il une bonne alternative?

@Mike Stone

Il est vrai que les Chaînes en Ruby sont mutables, mais cela ne signifie pas que nous devons toujours compter sur cette fonctionnalité. Si stuff est grande, les performances et les besoins en mémoire de cette, par exemple, est très mauvais.

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

La "bonne" façon de le faire en Java:

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

Bien que mon Java est un peu rouillé.

@Mike Stone #2: Whoah!!!!! Comment j'ai pu rater ça dans l'API? Je ne suis toujours pas sûr de la performance de StringIO, mais au moins, c'est maintenant "Ruby" problème, pas le mien :)

121voto

Mike Stone Points 21293

J'ai consulté la documentation Ruby pour StringIO , et il semble que ce que vous voulez, c'est StringIO#string , pas StringIO#to_s

Ainsi, changez votre code pour:

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

34voto

Colin Curtin Points 1087

Comme pour les autres objets de type IO dans Ruby, lorsque vous écrivez sur un IO, le pointeur de caractère avance.

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

Une autre façon de peler ce chat.

24voto

jmanrubia Points 515

J'ai fait quelques points de repère et l'approche la plus rapide utilise la méthode String#<< . L'utilisation de StringIO est un peu plus lente.

 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)
 

La concaténation de chaînes à l'aide de la méthode String#+ est l'approche la plus lente de plusieurs ordres de grandeur:

 >> 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)
 

Je pense donc que la bonne réponse est que l'équivalent de StringBuffer de Java utilise simplement String#<< en Ruby.

12voto

palmsey Points 3799

Votre exemple fonctionne en Ruby - je viens de l'essayer.

 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"
 

Sauf si je manque la raison pour laquelle vous utilisez to_s - qui ne fait que générer l'ID d'objet.

3voto

Mike Stone Points 21293

Eh bien, un StringBuffer est pas tout à fait comme nécessaire en Ruby, principalement parce que les Chaînes en Ruby sont mutables... ainsi, vous pouvez construire une chaîne de caractères par la modification de la chaîne existante plutôt que de construire de nouvelles cordes à chaque concat.

Comme une note, vous pouvez également utiliser la chaîne de caractères spéciaux syntaxe où vous pouvez construire une chaîne de caractères qui référence les autres variables à l'intérieur de la chaîne, ce qui le rend très lisible de la chaîne de construction. Considérer:

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

Ces chaînes peuvent également contenir des expressions, pas seulement les variables... tels que:

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

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X