关于字符串:为什么Ruby没有真正的StringBuffer或StringIO?

关于字符串:为什么Ruby没有真正的StringBuffer或StringIO?

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

我最近阅读了一篇关于在Ruby中使用StringIO的不错的文章。 不过,作者没有提及的是StringIO只是一个" I"。 没有" O"。 您不能这样做,例如:

1
2
3
4
5
6
7
s = StringIO.new
s << 'foo'
s << 'bar'
s.to_s
# => should be"foo
bar"
# => really is ''`

就像Java一样,Ruby确实需要一个StringBuffer。 StringBuffers有两个重要目的。 首先,他们让您测试Ruby的StringIO的一半输出。 其次,它们对于从小零件上构建长弦很有用-乔尔一遍又一遍地提醒我们,这非常慢。

有没有好的替代品?

确实,Ruby中的字符串是可变的,但这并不意味着我们应该始终依赖于该功能。 如果stuff大,则例如,其性能和内存要求确实很差。

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

在Java中执行此操作的"正确"方法是:

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

虽然我的Java有点生锈。


我查看了StringIO的ruby文档,看起来您想要的是StringIO#string,而不是StringIO#to_s

因此,将代码更改为:

1
2
3
4
s = StringIO.new
s << 'foo'
s << 'bar'
s.string

与Ruby中的其他IO类型对象一样,当您写入IO时,字符指针也会前进。

1
2
3
4
5
6
7
8
9
10
11
12
>> s = StringIO.new
=> #<StringIO:0x3659d4>
>> s << 'foo'
=> #<StringIO:0x3659d4>
>> s << 'bar'
=> #<StringIO:0x3659d4>
>> s.pos
=> 6
>> s.rewind
=> 0
>> s.read
=>"foobar"


我做了一些基准测试,最快的方法是使用String#<<方法。使用StringIO有点慢。

1
2
3
4
5
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#+方法连接字符串是许多数量级中最慢的方法:

1
2
3
4
5
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等效的只是在Ruby中使用String#<<


您的示例在Ruby中有效-我刚刚尝试过。

1
2
3
4
5
6
7
8
9
10
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。


好吧,StringBuffer在Ruby中并不是必需的,主要是因为Ruby中的字符串是可变的...因此,您可以通过修改现有字符串来构建字符串,而不是使用每个concat构造新的字符串。

需要注意的是,您还可以使用特殊的字符串语法,在该语法中,您可以构建一个引用该字符串中其他变量的字符串,从而使字符串的构造非常容易理解。考虑:

1
2
3
first ="Mike"
last ="Stone"
name ="#{first} #{last}"

这些字符串还可以包含表达式,而不仅仅是变量……例如:

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


推荐阅读