Ruby中的字符串连接

时间:2008-12-18 13:04:21

标签: ruby string-concatenation

我正在寻找一种更优雅的方式来连接Ruby中的字符串。

我有以下一行:

source = "#{ROOT_DIR}/" << project << "/App.config"

有没有更好的方法呢?

就此而言<<+之间的区别是什么?

16 个答案:

答案 0 :(得分:531)

您可以通过多种方式实现这一目标:

  1. 正如您使用<<所示,但这不是通常的方式
  2. 使用字符串插值

    source = "#{ROOT_DIR}/#{project}/App.config"
    
  3. +

    source = "#{ROOT_DIR}/" + project + "/App.config"
    
  4. 第二种方法在记忆/速度方面似乎比我看到的更有效(尽管没有测量)。当ROOT_DIR为零时,所有三种方法都会抛出未初始化的常量错误。

    在处理路径名时,您可能希望使用File.join以避免弄乱路径名分隔符。

    最后,这是一个品味问题。

答案 1 :(得分:90)

+运算符是正常的连接选择,可能是连接字符串的最快方法。

+<<之间的区别在于<<更改了左侧的对象,而+则没有。

irb(main):001:0> s = 'a'
=> "a"
irb(main):002:0> s + 'b'
=> "ab"
irb(main):003:0> s
=> "a"
irb(main):004:0> s << 'b'
=> "ab"
irb(main):005:0> s
=> "ab"

答案 2 :(得分:77)

如果你只是连接路径,你可以使用Ruby自己的File.join方法。

source = File.join(ROOT_DIR, project, 'App.config')

答案 3 :(得分:22)

来自http://greyblake.com/blog/2012/09/02/ruby-perfomance-tricks/

使用<< aka concat远比+=更有效,因为后者创建了一个时态对象并用新对象覆盖第一个对象。

require 'benchmark'

N = 1000
BASIC_LENGTH = 10

5.times do |factor|
  length = BASIC_LENGTH * (10 ** factor)
  puts "_" * 60 + "\nLENGTH: #{length}"

  Benchmark.bm(10, '+= VS <<') do |x|
    concat_report = x.report("+=")  do
      str1 = ""
      str2 = "s" * length
      N.times { str1 += str2 }
    end

    modify_report = x.report("<<")  do
      str1 = "s"
      str2 = "s" * length
      N.times { str1 << str2 }
    end

    [concat_report / modify_report]
  end
end

输出:

____________________________________________________________
LENGTH: 10
                 user     system      total        real
+=           0.000000   0.000000   0.000000 (  0.004671)
<<           0.000000   0.000000   0.000000 (  0.000176)
+= VS <<          NaN        NaN        NaN ( 26.508796)
____________________________________________________________
LENGTH: 100
                 user     system      total        real
+=           0.020000   0.000000   0.020000 (  0.022995)
<<           0.000000   0.000000   0.000000 (  0.000226)
+= VS <<          Inf        NaN        NaN (101.845829)
____________________________________________________________
LENGTH: 1000
                 user     system      total        real
+=           0.270000   0.120000   0.390000 (  0.390888)
<<           0.000000   0.000000   0.000000 (  0.001730)
+= VS <<          Inf        Inf        NaN (225.920077)
____________________________________________________________
LENGTH: 10000
                 user     system      total        real
+=           3.660000   1.570000   5.230000 (  5.233861)
<<           0.000000   0.010000   0.010000 (  0.015099)
+= VS <<          Inf 157.000000        NaN (346.629692)
____________________________________________________________
LENGTH: 100000
                 user     system      total        real
+=          31.270000  16.990000  48.260000 ( 48.328511)
<<           0.050000   0.050000   0.100000 (  0.105993)
+= VS <<   625.400000 339.800000        NaN (455.961373)

答案 4 :(得分:10)

由于这是一条路径,我可能会使用数组并加入:

source = [ROOT_DIR, project, 'App.config'] * '/'

答案 5 :(得分:7)

这是另一个受enter image description here启发的基准测试。它比较了动态和预定义字符串的连接(+),追加(<<)和插值(#{})。

require 'benchmark'

# we will need the CAPTION and FORMAT constants:
include Benchmark

count = 100_000


puts "Dynamic strings"

Benchmark.benchmark(CAPTION, 7, FORMAT) do |bm|
  bm.report("concat") { count.times { 11.to_s +  '/' +  12.to_s } }
  bm.report("append") { count.times { 11.to_s << '/' << 12.to_s } }
  bm.report("interp") { count.times { "#{11}/#{12}" } }
end


puts "\nPredefined strings"

s11 = "11"
s12 = "12"
Benchmark.benchmark(CAPTION, 7, FORMAT) do |bm|
  bm.report("concat") { count.times { s11 +  '/' +  s12 } }
  bm.report("append") { count.times { s11 << '/' << s12 } }
  bm.report("interp") { count.times { "#{s11}/#{s12}"   } }
end

输出:

Dynamic strings
              user     system      total        real
concat    0.050000   0.000000   0.050000 (  0.047770)
append    0.040000   0.000000   0.040000 (  0.042724)
interp    0.050000   0.000000   0.050000 (  0.051736)

Predefined strings
              user     system      total        real
concat    0.030000   0.000000   0.030000 (  0.024888)
append    0.020000   0.000000   0.020000 (  0.023373)
interp    3.160000   0.160000   3.320000 (  3.311253)

结论:MRI中的插值很重。

答案 6 :(得分:6)

我更喜欢使用路径名:

require 'pathname' # pathname is in stdlib
Pathname(ROOT_DIR) + project + 'App.config'

来自ruby docs的<<+

+:返回包含与str连接的other_str的 new 字符串

<<:将给定对象连接到str。如果对象是0到255之间的Fixnum,则在连接之前将其转换为字符。

因此,第一个操作数变为差异(<<进行了更改,+返回新字符串,因此内存更重)如果第一个操作数是Fixnum({{1}将添加好像是代码等于该数字的字符,<<将引发错误)

答案 7 :(得分:6)

让我向你展示我的所有经验。

我有一个返回32k记录的查询,对于每个记录,我调用一种方法将该数据库记录格式化为格式化字符串,然后将其连接成一个String,在所有这个过程结束时将转换成一个文件磁盘。

我的问题是,通过记录,大约24k,连接字符串的过程导致痛苦。

我是使用常规'+'运算符来做的。

当我改为'&lt;&lt;&lt;就像魔术一样。真的很快。

所以,我记得我的旧时代 - 有点像1998年 - 当我使用Java并使用'+'连接String并从String更改为StringBuffer时(现在我们Java开发人员拥有StringBuilder)。

我相信+ /&lt;&lt;的过程在Ruby世界中,与Java世界中的+ / StringBuilder.append相同。

第一个在内存中重新分配整个对象,另一个只指向一个新地址。

答案 8 :(得分:5)

以下是更多方法:

"String1" + "String2"

"#{String1} #{String2}"

String1<<String2

等等......

答案 9 :(得分:5)

你说连接?那么#concat方法呢?

a = 'foo'
a.object_id #=> some number
a.concat 'bar' #=> foobar
a.object_id #=> same as before -- string a remains the same object

平心而论,concat别名为<<

答案 10 :(得分:3)

您可以使用+<<运算符,但在ruby中,.concat函数是最可取的函数,因为它比其他运算符要快得多。您可以像这样使用它。

source = "#{ROOT_DIR}/".concat(project.concat.("/App.config"))

答案 11 :(得分:1)

您还可以如下使用%

source = "#{ROOT_DIR}/%s/App.config" % project

此方法也可以与'(单引号)一起使用。

答案 12 :(得分:0)

情况很重要,例如:

# this will not work
output = ''

Users.all.each do |user|
  output + "#{user.email}\n"
end
# the output will be ''
puts output

# this will do the job
output = ''

Users.all.each do |user|
  output << "#{user.email}\n"
end
# will get the desired output
puts output

在第一个示例中,与+运算符的连接不会更新output对象,但是,在第二个示例中,<<运算符将更新output对象每次迭代。因此,对于上述情况,<<更好。

答案 13 :(得分:0)

您可以直接在字符串定义中串联:

nombre_apellido = "#{customer['first_name']} #{customer['last_name']} #{order_id}"

答案 14 :(得分:0)

对于您的特殊情况,在构造字符串的文件路径类型时,也可以使用> SourceSet with name 'main' not found.

Array#join

将不同类型自动转换为字符串具有令人愉快的副作用:

string = [ROOT_DIR, project, 'App.config'].join('/')]

答案 15 :(得分:0)

对于木偶:

$username = 'lala'
notify { "Hello ${username.capitalize}":
    withpath => false,
}