Concatenazione di stringhe in Ruby


Risposte:


575

Puoi farlo in diversi modi:

  1. Come mostrato con <<ma che non è il solito modo
  2. Con interpolazione di stringhe

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

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

Il secondo metodo sembra essere più efficiente in termini di memoria / velocità rispetto a quello che ho visto (non misurato però). Tutti e tre i metodi genereranno un errore costante non inizializzato quando ROOT_DIR è zero.

Quando hai a che fare con i nomi dei percorsi, potresti voler usare File.joinper evitare di sbagliare con il separatore dei nomi di percorso.

Alla fine, è una questione di gusti.


7
Non ho molta esperienza con il rubino. Ma generalmente nei casi in cui si concatenano molte stringhe, spesso è possibile ottenere prestazioni aggiungendo le stringhe a un array e quindi alla fine unendo atomicamente la stringa. Quindi << potrebbe essere utile?
PEZ,

1
Dovrai aggiungere memoria e copiare comunque la stringa più lunga. << è più o meno lo stesso di + tranne che puoi << con un singolo carattere.
Keltia,

9
Invece di usare << sugli elementi di un array, usa Array # join, è molto più veloce.
Grant Hutchins,

94

L' +operatore è la normale scelta di concatenazione ed è probabilmente il modo più veloce per concatenare le stringhe.

La differenza tra +e <<è che <<cambia l'oggetto sul lato sinistro e +non lo fa.

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"

32
L'operatore + non è sicuramente il modo più veloce per concatenare le stringhe. Ogni volta che lo usi, ne fa una copia, mentre << si concatena sul posto ed è molto più performante.
Evil Trout,

5
Per la maggior parte degli usi, l'interpolazione +e <<saranno più o meno gli stessi. Se hai a che fare con molte stringhe, o molto grandi, potresti notare una differenza. Sono stato sorpreso da quanto simili si sono esibiti. gist.github.com/2895311
Matt Burke

8
I tuoi risultati jruby sono distorti dall'interpolazione dal sovraccarico JVM in fase iniziale. Se esegui la suite di test più volte (nello stesso processo, quindi avvolgi tutto in un 5.times do ... endblocco) per ciascun interprete, otterrai risultati più accurati. I miei test hanno dimostrato che l'interpolazione è il metodo più veloce, tra tutti gli interpreti di Ruby. Mi sarei aspettato <<di essere il più veloce, ma è per questo che facciamo un benchmark.
Womble,

Non essendo troppo esperto su Ruby, sono curioso di sapere se la mutazione viene eseguita in pila o in pila? Se in heap, anche un'operazione di mutazione, che sembra dovrebbe essere più veloce, probabilmente comporta una qualche forma di malloc. Senza di essa, mi aspetterei un overflow del buffer. L'uso dello stack potrebbe essere piuttosto veloce, ma il valore risultante viene probabilmente inserito comunque nell'heap, richiedendo un'operazione malloc. Alla fine, mi aspetto che il puntatore di memoria sia un nuovo indirizzo, anche se il riferimento alla variabile lo fa sembrare una mutazione sul posto. Quindi, davvero, c'è una differenza?
Robin Coe,

79

Se stai solo concatenando percorsi puoi usare il metodo File.join di Ruby.

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

5
Questa sembra essere la strada da percorrere da allora in poi ruby ​​si occuperà di creare la stringa corretta sul sistema con diversi separatori di percorso.
PEZ,

26

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

L'uso di <<aka concatè molto più efficiente di +=, poiché quest'ultimo crea un oggetto temporale e sovrascrive il primo oggetto con il nuovo oggetto.

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

produzione:

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

11

Dato che questo è un percorso, probabilmente userò array e join:

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

9

Ecco un altro punto di riferimento ispirato a questa sostanza . Confronta concatenazione ( +), aggiunta ( <<) e interpolazione ( #{}) per stringhe dinamiche e predefinite.

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

produzione:

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)

Conclusione: l'interpolazione nella risonanza magnetica è pesante.


Poiché ora le stringhe stanno iniziando a essere immutabili, mi piacerebbe vedere un nuovo punto di riferimento per questo.
bibstha,

7

Preferirei usare Pathname:

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

su <<e +dai documenti rubini:

+: Restituisce una nuova stringa contenente other_str concatenato a str

<<: Concatena l'oggetto dato a str. Se l'oggetto è un Fixnum tra 0 e 255, viene convertito in un carattere prima della concatenazione.

quindi la differenza è in ciò che diventa il primo operando ( <<apporta modifiche al posto, +restituisce una nuova stringa in modo che sia memoria più pesante) e cosa sarà se il primo operando è Fixnum ( <<aggiungerà come se fosse un carattere con codice uguale a quel numero, +aumenterà errore)


2
Ho appena scoperto che chiamare '+' su un nome di percorso può essere pericoloso perché se l'arg è un percorso assoluto, il percorso del ricevitore viene ignorato: Pathname('/home/foo') + '/etc/passwd' # => #<Pathname:/etc/passwd>. Questo è di progettazione, basato sull'esempio di rubydoc. Sembra che File.join sia più sicuro.
Kelvin,

inoltre è necessario chiamare (Pathname(ROOT_DIR) + project + 'App.config').to_sse si desidera restituire un oggetto stringa.
lacostenycoder,

6

Lascia che ti mostri tutta la mia esperienza con quello.

Ho avuto una query che ha restituito 32k di record, per ogni record ho chiamato un metodo per formattare il record del database in una stringa formattata e poi concatenarlo in una stringa che alla fine di tutto questo processo si trasformerà in un file sul disco.

Il mio problema era che, per la cronaca, il processo di concatenazione della stringa ha provocato un dolore.

Lo stavo facendo usando il normale operatore '+'.

Quando sono passato a '<<' era come per magia. È stato davvero veloce.

Quindi, mi sono ricordato dei miei vecchi tempi - una specie di 1998 - quando stavo usando Java e concatenando String usando '+' e sono passato da String a StringBuffer (e ora noi sviluppatori Java abbiamo StringBuilder).

Credo che il processo di + / << nel mondo Ruby sia lo stesso di + / StringBuilder.append nel mondo Java.

Il primo rialloca l'intero oggetto in memoria e l'altro punta semplicemente a un nuovo indirizzo.


5

Concatenazione dici? Che ne dici di #concatmetodo allora?

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

In tutta onestà, concatè alias come <<.


7
Esiste un altro modo per incollare le corde non menzionate da altri, e cioè per giustapposizione:"foo" "bar" 'baz" #=> "foobarabaz"
Boris Stitnicky,

Nota per gli altri: questa non dovrebbe essere una singola citazione, ma una doppia come le altre. Metodo accurato!
Joshua Pinter,

5

Ecco altri modi per farlo:

"String1" + "String2"

"#{String1} #{String2}"

String1<<String2

E così via ...


2

Puoi anche usare %come segue:

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

Questo approccio funziona anche con 'virgolette (singole).


2

È possibile utilizzare +o l' <<operatore, ma nella .concatfunzione rubino è la più preferibile, poiché è molto più veloce di altri operatori. Puoi usarlo come.

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

Penso che tu abbia un extra .dopo l'ultimo concatno?
lacostenycoder,

1

La situazione è importante, ad esempio:

# 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

Nel primo esempio, la concatenazione con l' +operatore non aggiornerà l' outputoggetto, tuttavia, nel secondo esempio, l' <<operatore aggiornerà l' outputoggetto ad ogni iterazione. Quindi, per il tipo di situazione di cui sopra, <<è meglio.


1

È possibile concatenare direttamente nella definizione della stringa:

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

0

Per il tuo caso particolare puoi usare anche Array#joinquando costruisci il tipo di stringa del percorso del file:

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

Ciò ha un piacevole effetto collaterale di convertire automaticamente diversi tipi in stringa:

['foo', :bar, 1].join('/')
=>"foo/bar/1"

0

Per marionette:

$username = 'lala'
notify { "Hello ${username.capitalize}":
    withpath => false,
}
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.