Qual è il modo più semplice per rimuovere il primo carattere da una stringa?


174

Esempio:

[12,23,987,43

Qual è il modo più veloce ed efficiente per rimuovere il " [", usando forse un chop()ma per il primo personaggio?


1
Ho modificato la mia risposta, quindi potrebbe essere possibile modificare la risposta selezionata. Vedi se riesci ad assegnarlo alla risposta di Jason Stirk poiché la sua è la più veloce ed è molto leggibile.
Tin Man,

3
Usa str [1 ..- 1], il più veloce in base alle risposte di seguito.
Achyut Rastogi,

1
A partire da Ruby 2.5 è possibile utilizzare delete_prefixe delete_prefix!- ulteriori dettagli di seguito . Non ho avuto il tempo di fare il benchmark, ma lo farò presto!
SRack,

Aggiornamento: ho confrontato i nuovi metodi ( delete_prefix\ delete_prefix!) e sono abbastanza veloci. Non delineano i preferiti precedenti per la velocità, ma la leggibilità significa che sono nuove fantastiche opzioni da avere!
SRack

Risposte:


233

Preferisco usare qualcosa come:

asdf = "[12,23.987,43"
asdf [0] = '' 

p asdf
# >> "12,23.987,43"

Sono sempre alla ricerca del modo più veloce e più leggibile di fare le cose:

require 'benchmark'

N = 1_000_000

puts RUBY_VERSION

STR = "[12,23,987,43"

Benchmark.bm(7) do |b|
  b.report('[0]') { N.times { "[12,23,987,43"[0] = '' } }
  b.report('sub') { N.times { "[12,23,987,43".sub(/^\[+/, "") } }

  b.report('gsub') { N.times { "[12,23,987,43".gsub(/^\[/, "") } }
  b.report('[1..-1]') { N.times { "[12,23,987,43"[1..-1] } }
  b.report('slice') { N.times { "[12,23,987,43".slice!(0) } }
  b.report('length') { N.times { "[12,23,987,43"[1..STR.length] } }

end

In esecuzione sul mio Mac Pro:

1.9.3
              user     system      total        real
[0]       0.840000   0.000000   0.840000 (  0.847496)
sub       1.960000   0.010000   1.970000 (  1.962767)
gsub      4.350000   0.020000   4.370000 (  4.372801)
[1..-1]   0.710000   0.000000   0.710000 (  0.713366)
slice     1.020000   0.000000   1.020000 (  1.020336)
length    1.160000   0.000000   1.160000 (  1.157882)

Aggiornamento per incorporare un'altra risposta suggerita:

require 'benchmark'

N = 1_000_000

class String
  def eat!(how_many = 1)
    self.replace self[how_many..-1]
  end

  def first(how_many = 1)
    self[0...how_many]
  end

  def shift(how_many = 1)
    shifted = first(how_many)
    self.replace self[how_many..-1]
    shifted
  end
  alias_method :shift!, :shift
end

class Array
  def eat!(how_many = 1)
    self.replace self[how_many..-1]
  end
end

puts RUBY_VERSION

STR = "[12,23,987,43"

Benchmark.bm(7) do |b|
  b.report('[0]') { N.times { "[12,23,987,43"[0] = '' } }
  b.report('sub') { N.times { "[12,23,987,43".sub(/^\[+/, "") } }

  b.report('gsub') { N.times { "[12,23,987,43".gsub(/^\[/, "") } }
  b.report('[1..-1]') { N.times { "[12,23,987,43"[1..-1] } }
  b.report('slice') { N.times { "[12,23,987,43".slice!(0) } }
  b.report('length') { N.times { "[12,23,987,43"[1..STR.length] } }
  b.report('eat!') { N.times { "[12,23,987,43".eat! } }
  b.report('reverse') { N.times { "[12,23,987,43".reverse.chop.reverse } }
end

Che si traduce in:

2.1.2
              user     system      total        real
[0]       0.300000   0.000000   0.300000 (  0.295054)
sub       0.630000   0.000000   0.630000 (  0.631870)
gsub      2.090000   0.000000   2.090000 (  2.094368)
[1..-1]   0.230000   0.010000   0.240000 (  0.232846)
slice     0.320000   0.000000   0.320000 (  0.320714)
length    0.340000   0.000000   0.340000 (  0.341918)
eat!      0.460000   0.000000   0.460000 (  0.452724)
reverse   0.400000   0.000000   0.400000 (  0.399465)

E un altro utilizzo /^./per trovare il primo personaggio:

require 'benchmark'

N = 1_000_000

class String
  def eat!(how_many = 1)
    self.replace self[how_many..-1]
  end

  def first(how_many = 1)
    self[0...how_many]
  end

  def shift(how_many = 1)
    shifted = first(how_many)
    self.replace self[how_many..-1]
    shifted
  end
  alias_method :shift!, :shift
end

class Array
  def eat!(how_many = 1)
    self.replace self[how_many..-1]
  end
end

puts RUBY_VERSION

STR = "[12,23,987,43"

Benchmark.bm(7) do |b|
  b.report('[0]') { N.times { "[12,23,987,43"[0] = '' } }
  b.report('[/^./]') { N.times { "[12,23,987,43"[/^./] = '' } }
  b.report('[/^\[/]') { N.times { "[12,23,987,43"[/^\[/] = '' } }
  b.report('sub+') { N.times { "[12,23,987,43".sub(/^\[+/, "") } }
  b.report('sub') { N.times { "[12,23,987,43".sub(/^\[/, "") } }
  b.report('gsub') { N.times { "[12,23,987,43".gsub(/^\[/, "") } }
  b.report('[1..-1]') { N.times { "[12,23,987,43"[1..-1] } }
  b.report('slice') { N.times { "[12,23,987,43".slice!(0) } }
  b.report('length') { N.times { "[12,23,987,43"[1..STR.length] } }
  b.report('eat!') { N.times { "[12,23,987,43".eat! } }
  b.report('reverse') { N.times { "[12,23,987,43".reverse.chop.reverse } }
end

Che si traduce in:

# >> 2.1.5
# >>               user     system      total        real
# >> [0]       0.270000   0.000000   0.270000 (  0.270165)
# >> [/^./]    0.430000   0.000000   0.430000 (  0.432417)
# >> [/^\[/]   0.460000   0.000000   0.460000 (  0.458221)
# >> sub+      0.590000   0.000000   0.590000 (  0.590284)
# >> sub       0.590000   0.000000   0.590000 (  0.596366)
# >> gsub      1.880000   0.010000   1.890000 (  1.885892)
# >> [1..-1]   0.230000   0.000000   0.230000 (  0.223045)
# >> slice     0.300000   0.000000   0.300000 (  0.299175)
# >> length    0.320000   0.000000   0.320000 (  0.325841)
# >> eat!      0.410000   0.000000   0.410000 (  0.409306)
# >> reverse   0.390000   0.000000   0.390000 (  0.393044)

Ecco un altro aggiornamento su hardware più veloce e una versione più recente di Ruby:

2.3.1
              user     system      total        real
[0]       0.200000   0.000000   0.200000 (  0.204307)
[/^./]    0.390000   0.000000   0.390000 (  0.387527)
[/^\[/]   0.360000   0.000000   0.360000 (  0.360400)
sub+      0.490000   0.000000   0.490000 (  0.492083)
sub       0.480000   0.000000   0.480000 (  0.487862)
gsub      1.990000   0.000000   1.990000 (  1.988716)
[1..-1]   0.180000   0.000000   0.180000 (  0.181673)
slice     0.260000   0.000000   0.260000 (  0.266371)
length    0.270000   0.000000   0.270000 (  0.267651)
eat!      0.400000   0.010000   0.410000 (  0.398093)
reverse   0.340000   0.000000   0.340000 (  0.344077)

Perché gsub è così lento?

Dopo aver effettuato una ricerca / sostituzione, gsubdeve verificare eventuali corrispondenze aggiuntive prima di poter sapere se è terminato. subfa solo uno e finisce. Considera gsubche è un minimo di due subchiamate.

Inoltre, è importante ricordarlo gsub, e subpuò anche essere handicappato da regex scritto male che si abbina molto più lentamente di una ricerca di sottostringa. Se possibile, ancorare il regex per ottenere la massima velocità da esso. Ci sono risposte qui su Stack Overflow che lo dimostrano, quindi cerca in giro se vuoi maggiori informazioni.


34
È importante notare che questo funzionerà solo in Ruby 1.9. In Ruby 1.8, questo rimuoverà il primo byte dalla stringa, non il primo carattere, che non è quello che l'OP vuole.
Jörg W Mittag,

+1: dimentico sempre che a una posizione di stringa puoi assegnare non solo un singolo carattere, ma puoi anche inserire una sottostringa. Grazie!
quetzalcoatl,

"[12,23,987,43".delete "["
rupweb,

4
Ciò lo elimina da tutte le posizioni, che non è quello che l'OP voleva: "... per il primo personaggio?".
Tin Man,

2
" what about "[12,23,987,43".shift ?"? Che dire di "[12,23,987,43".shift NoMethodError: undefined method shift 'for "[12,23,987,43": String`?
Tin Man,

293

Simile alla risposta di Pablo sopra, ma un detergente per l'ombra:

str[1..-1]

Restituirà l'array da 1 all'ultimo carattere.

'Hello World'[1..-1]
 => "ello World"

13
+1 Dai un'occhiata ai risultati del benchmark che ho aggiunto alla mia risposta. Hai il tempo di esecuzione più veloce, inoltre penso che sia molto pulito.
Tin Man,

Che dire delle prestazioni str[1,]rispetto a quanto sopra?
Bohr,

1
@Bohr: str[1,]restituisci il 2 ° carattere poiché l'intervallo è 1:nil. Dovresti fornire la lunghezza calcolata effettiva o qualcosa di garantito superiore alla lunghezza, ad esempio str[1,999999](usa int_max ovviamente) per ottenere l'intera coda. [1..-1]è più pulito e probabilmente più veloce, poiché non è necessario operare manualmente sulla lunghezza (vedere [1..length] nel benchmark)
quetzalcoatl

4
Soluzione molto bella. A proposito, se si desidera rimuovere il primo e l'ultimo personaggio:str[1..-2]
pisaruk,

50

Possiamo usare slice per fare questo:

val = "abc"
 => "abc" 
val.slice!(0)
 => "a" 
val
 => "bc" 

Utilizzando slice!possiamo eliminare qualsiasi carattere specificandone l'indice.


2
Questo elegante slice!(0)dovrebbe davvero essere la risposta selezionata, poiché usare asdf[0] = '' per rimuovere il primo personaggio è ridicolo (proprio come usare gsub con regex e sparare una mosca con un obice).
f055,

1
Sebbene possa sembrare poco intuitivo in superficie, []=non richiede tanto codice C sottostante, dove slice!richiede ulteriore lavoro. Questo aggiunge. L'argomento potrebbe essere "Qual è più leggibile?" Trovo che sia []=leggibile, ma vengo da uno sfondo C -> Perl che probabilmente colora il mio modo di pensare. Gli sviluppatori Java probabilmente penserebbero che sia meno leggibile. Uno dei due è un modo accettabile per eseguire l'attività fintanto che è facilmente comprensibile e gestibile e non carica indebitamente la CPU.
Tin Man,

Ok. Sai come possiamo misurare se una funzione richiede molto carico della CPU in ROR? o dovremmo usare la differenza del tempo di esecuzione in milli o nanosecondi?
Balanv,

18

Rubino 2.5+

A partire da Ruby 2.5 è possibile utilizzare delete_prefixo delete_prefix!per raggiungere questo obiettivo in modo leggibile.

In questo caso "[12,23,987,43".delete_prefix("[").

Maggiori informazioni qui:

'invisible'.delete_prefix('in') #=> "visible"
'pink'.delete_prefix('in') #=> "pink"

NB puoi anche usarlo per rimuovere elementi dalla fine di una stringa con delete_suffixedelete_suffix!

'worked'.delete_suffix('ed') #=> "work"
'medical'.delete_suffix('ed') #=> "medical"

Modificare:

Utilizzando l'impostazione benchmark di Tin Man, sembra anche abbastanza veloce (sotto le ultime due voci delete_pe delete_p!). Non rispecchia abbastanza i precedenti favoriti per la velocità, anche se è molto leggibile.

2.5.0
              user     system      total        real
[0]       0.174766   0.000489   0.175255 (  0.180207)
[/^./]    0.318038   0.000510   0.318548 (  0.323679)
[/^\[/]   0.372645   0.001134   0.373779 (  0.379029)
sub+      0.460295   0.001510   0.461805 (  0.467279)
sub       0.498351   0.001534   0.499885 (  0.505729)
gsub      1.669837   0.005141   1.674978 (  1.682853)
[1..-1]   0.199840   0.000976   0.200816 (  0.205889)
slice     0.279661   0.000859   0.280520 (  0.285661)
length    0.268362   0.000310   0.268672 (  0.273829)
eat!      0.341715   0.000524   0.342239 (  0.347097)
reverse   0.335301   0.000588   0.335889 (  0.340965)
delete_p  0.222297   0.000832   0.223129 (  0.228455)
delete_p!  0.225798   0.000747   0.226545 (  0.231745)


14

Se si desidera sempre eliminare le parentesi principali:

"[12,23,987,43".gsub(/^\[/, "")

Se vuoi solo rimuovere il primo carattere e sai che non sarà in un set di caratteri multibyte:

"[12,23,987,43"[1..-1]

o

"[12,23,987,43".slice(1..-1)

1
Userei "[12,23,987,43".sub(/^\[+/, "")invece di gsub(/^\[/, ""). Il primo consente al motore regex di trovare tutte le partite, quindi vengono sostituite in una sola azione e si traduce in un miglioramento della velocità di circa 2x con Ruby 1.9.3.
Tin Man,

1
Dato che abbiamo a che fare con le stringhe, dovrebbe essere gsub(/\A\[/, "") ?
Sagar Pandya,

6

Alternativa inefficiente:

str.reverse.chop.reverse

4

Ad esempio: a = "One Two Three"

1.9.2-p290 > a = "One Two Three"
 => "One Two Three" 

1.9.2-p290 > a = a[1..-1]
 => "ne Two Three" 

1.9.2-p290 > a = a[1..-1]
 => "e Two Three" 

1.9.2-p290 > a = a[1..-1]
 => " Two Three" 

1.9.2-p290 > a = a[1..-1]
 => "Two Three" 

1.9.2-p290 > a = a[1..-1]
 => "wo Three" 

In questo modo è possibile rimuovere uno alla volta il primo carattere della stringa.


3
Questa è la stessa della risposta di Jason Stirk, solo la sua è stata presentata molti mesi prima.
Tin Man,

3

Modo semplice:

str = "[12,23,987,43"

removed = str[1..str.length]

Modo fantastico:

class String
  def reverse_chop()
    self[1..self.length]
  end
end

"[12,23,987,43".reverse_chop()

(Nota: preferisci il modo semplice :))


1
Se vuoi preservare la semantica "chop" potresti semplicemente"[12,23,987,43".reverse.chop.reverse
Chris Heald,

questa è una prestazione abbastanza grande solo per spogliare un personaggio
Pablo Fernandez il

7
perché non usare [1 ..- 1] anziché [1..self.length]?
Horseyguy,

L'esempio di patching delle scimmie è abbastanza fuori per questa domanda, è semplicemente irrilevante e brutto IMO.
dredozubov,

3

Grazie a @ the-tin-man per aver messo insieme i parametri di riferimento!

Ahimè, non mi piace molto nessuna di quelle soluzioni. O richiedono un ulteriore passaggio per ottenere il risultato ( [0] = '', .strip!) o non sono molto semantici / chiari su ciò che sta accadendo ( [1..-1]: "Uhm, un intervallo da 1 a 1 negativo?"), Oppure sono lenti o lunghi a scrivere ( .gsub, .length).

Quello che stiamo tentando è un "turno" (nel linguaggio dell'array), ma restituendo i personaggi rimanenti, piuttosto che ciò che è stato spostato. Usiamo il nostro Ruby per renderlo possibile con le stringhe! Possiamo usare l'operazione di parentesi veloce, ma dare un buon nome e prendere un argomento per specificare quanto vogliamo tagliare in avanti:

class String
  def eat!(how_many = 1)
    self.replace self[how_many..-1]
  end
end

Ma c'è di più che possiamo fare con quell'operazione di staffa veloce ma ingombrante. Mentre ci siamo, per completezza, scriviamo a #shifte #firstper String (perché Array dovrebbe avere tutto il divertimento‽‽), prendendo un argomento per specificare quanti caratteri vogliamo rimuovere dall'inizio:

class String
  def first(how_many = 1)
    self[0...how_many]
  end

  def shift(how_many = 1)
    shifted = first(how_many)
    self.replace self[how_many..-1]
    shifted
  end
  alias_method :shift!, :shift
end

Ok, ora abbiamo un buon modo chiaro di estrarre i caratteri dalla parte anteriore di una stringa, con un metodo che è coerente con Array#firste Array#shift(che dovrebbe davvero essere un metodo bang ??). E possiamo facilmente ottenere anche la stringa modificata con #eat!. Dovremmo condividere il nostro nuovo eat!potere ing con Array? Perchè no!

class Array
  def eat!(how_many = 1)
    self.replace self[how_many..-1]
  end
end

Ora possiamo:

> str = "[12,23,987,43" #=> "[12,23,987,43"
> str.eat!              #=> "12,23,987,43"
> str                   #=> "12,23,987,43"

> str.eat!(3)           #=> "23,987,43"
> str                   #=> "23,987,43"

> str.first(2)          #=> "23"
> str                   #=> "23,987,43"

> str.shift!(3)         #=> "23,"
> str                   #=> "987,43"

> arr = [1,2,3,4,5]     #=> [1, 2, 3, 4, 5] 
> arr.eat!              #=> [2, 3, 4, 5] 
> arr                   #=> [2, 3, 4, 5] 

Così va meglio!


2
Ricordo una discussione anni fa nei forum Perl su una tale funzione con il nome di chip()invece di chop()(e chimp()come analogo di chomp()).
Mark Thomas,

2
str = "[12,23,987,43"

str[0] = ""

7
È importante notare che questo funzionerà solo in Ruby 1.9. In Ruby 1.8, questo rimuoverà il primo byte dalla stringa, non il primo carattere, che non è quello che l'OP vuole.
Jörg W Mittag,

0
class String
  def bye_felicia()
    felicia = self.strip[0] #first char, not first space.
    self.sub(felicia, '')
  end
end

0

Usando regex:

str = 'string'
n = 1  #to remove first n characters

str[/.{#{str.size-n}}\z/] #=> "tring"

0

Trovo una buona soluzione str.delete(str[0])per la sua leggibilità, anche se non posso attestare le sue prestazioni.


0

list = [1,2,3,4] list.drop (1)

# => [2,3,4]

Elenco elimina uno o più elementi dall'inizio dell'array, non muta l'array e restituisce l'array stesso anziché l'elemento rilasciato.

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.