Qual é a maneira mais fácil de remover o primeiro caractere de uma string?

174

Exemplo:

[12,23,987,43

Qual é a maneira mais rápida e eficiente de remover o " [", usando talvez um chop()mas para o primeiro caractere?

NullVoxPopuli
fonte
1
Eu editei minha resposta, para que seja possível alterar a resposta selecionada. Veja se você pode atribuir a resposta de Jason Stirk, já que a dele é a mais rápida e muito legível.
the Tin Man
3
Use str [1 ..- 1], o mais rápido, de acordo com as respostas abaixo.
Achyut Rastogi
1
A partir do Ruby 2.5, você pode usar delete_prefixe delete_prefix!- mais detalhes abaixo . Ainda não tive tempo de avaliar, mas o farei em breve!
SRack
Atualização: comparei os novos métodos ( delete_prefix\ delete_prefix!) e eles são bem rápidos. Eles não são exatamente os favoritos anteriores em termos de velocidade, mas a legibilidade significa que são ótimas novas opções!
SRack

Respostas:

233

Eu meio que gosto de usar algo como:

asdf = "[12,23,987,43"
asdf [0] = '' 

p asdf
# >> "12,23,987,43"

Estou sempre procurando a maneira mais rápida e legível de fazer as coisas:

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

Executando no meu 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)

Atualizando para incorporar mais uma resposta sugerida:

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

O que resulta em:

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 outro usando /^./para encontrar o primeiro caractere:

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

O que resulta em:

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

Aqui está outra atualização de hardware mais rápido e uma versão mais recente do 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)

Por que o gsub é tão lento?

Depois de fazer uma pesquisa / substituição, gsubé necessário verificar possíveis correspondências adicionais antes de saber se está finalizado. subsó faz um e termina. Considere gsubum mínimo de duas subchamadas.

Além disso, é importante lembrar que gsub, e subtambém pode ser prejudicado por regex mal escrito que corresponder muito mais lentamente do que uma busca sub-string. Se possível, ancore o regex para obter o máximo de velocidade dele. Há respostas aqui no Stack Overflow demonstrando que, então, procure se deseja mais informações.

o homem de lata
fonte
34
É importante notar que isso só funcionará no Ruby 1.9. No Ruby 1.8, isso removerá o primeiro byte da string, não o primeiro caractere, que não é o que o OP deseja.
Jörg W Mittag
+1: sempre esqueço que, para uma posição de string, você pode atribuir não apenas um caractere, mas também pode inserir uma substring. Obrigado!
Quetzalcoatl
"[12,23,987,43".delete "["
rupweb
4
Isso o exclui de todas as posições, o que não é o que o OP queria: "... para o primeiro personagem?".
the Tin Man
2
" what about "[12,23,987,43".shift ?"? E quanto a "[12,23,987,43".shift NoMethodError: undefined method shift 'for "[12,23,987,43": String`?
o homem de lata
293

Semelhante à resposta de Pablo acima, mas um limpador de sombra:

str[1..-1]

Retornará a matriz de 1 até o último caractere.

'Hello World'[1..-1]
 => "ello World"
Jason Stirk
fonte
13
+1 Veja os resultados de referência que adicionei à minha resposta. Você tem o tempo de execução mais rápido, e acho que é muito limpo.
the Tin Man
E o desempenho do str[1,]comparado com o acima?
Bohr
1
@ Bohr: str[1,]retorna o segundo caractere, pois o intervalo é 1:nil. Você precisaria fornecer o comprimento calculado real ou algo garantido para ser maior que o comprimento, como str[1,999999](use int_max, é claro) para obter a cauda inteira. [1..-1]é mais limpo e provavelmente mais rápido, já que você não precisa para operar no comprimento manualmente (veja [1..length] no benchmark)
quetzalcoatl
4
Solução muito boa. A propósito, se alguém quiser remover o primeiro e o último caracteres:str[1..-2]
pisaruk 19/12/2014
50

Podemos usar a fatia para fazer isso:

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

Usando slice!podemos excluir qualquer caractere especificando seu índice.

balanv
fonte
2
Essa elegante slice!(0)deve realmente ser a resposta selecionada, pois usar asdf[0] = '' para remover o primeiro caractere é ridículo (assim como usar gsub com regex e disparar uma mosca com um obus).
F055 15/03/16
1
Embora possa parecer pouco intuitivo na superfície, []=não requer tanto código C subjacente, onde slice!requer trabalho adicional. Isso acrescenta-se. O argumento pode ser "Qual é mais legível?" Acho que usar []=legível, mas estou vindo de um plano de fundo C -> Perl que provavelmente colore meu pensamento. Os desenvolvedores de Java provavelmente acham que é menos legível. Uma das maneiras é aceitável para realizar a tarefa, desde que seja fácil de entender e manter e não carregue a CPU indevidamente.
the Tin Man
Está bem. Você sabe como podemos medir se uma função leva muita carga de CPU no ROR? ou devemos usar a diferença de tempo de execução em mili ou nanossegundos?
balanv
18

Ruby 2.5+

A partir do Ruby 2.5, você pode usar delete_prefixou delete_prefix!conseguir isso de maneira legível.

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

Mais informações aqui:

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

NB, você também pode usá-lo para remover itens do final de uma string com delete_suffixedelete_suffix!

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

Editar:

Usando a configuração de benchmark do Tin Man, parece muito rápido também (nas duas últimas entradas delete_pe delete_p!). Não dá muito certo aos favoritos anteriores por velocidade, embora seja muito legível.

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)
SRack
fonte
17

Eu prefiro isso:

str = "[12,23,987,43"
puts str[1..-1]
>> 12,23,987,43
henriquesuda
fonte
3
Convém verificar outras respostas antes de repeti-las. Isso já foi sugerido por stackoverflow.com/a/3614642/128421
the Tin Man
14

Se você sempre deseja remover os colchetes à esquerda:

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

Se você apenas deseja remover o primeiro caractere e sabe que ele não estará em um conjunto de caracteres multibyte:

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

ou

"[12,23,987,43".slice(1..-1)
Chris Heald
fonte
1
Eu usaria em "[12,23,987,43".sub(/^\[+/, "")vez de gsub(/^\[/, ""). O primeiro permite que o mecanismo regex encontre todas as correspondências e, em seguida, elas são substituídas em uma ação e resulta em uma melhoria de velocidade em 2x com o Ruby 1.9.3.
the Tin Man
1
Já que estamos lidando com strings, deveria ser isso gsub(/\A\[/, "") ?
Sagar Pandya
6

Alternativa ineficiente:

str.reverse.chop.reverse
jaamun
fonte
4

Por exemplo: 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" 

Dessa maneira, você pode remover um por um primeiro caractere da sequência.

Rubyist
fonte
3
É o mesmo que a resposta de Jason Stirk, que apenas a dele foi enviada muitos meses antes.
the Tin Man
3

Jeito fácil:

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

removed = str[1..str.length]

Maneira impressionante:

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

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

(Nota: prefira o caminho mais fácil :))

Pablo Fernandez
fonte
1
Se você quiser preservar a semântica "chop", você pode:"[12,23,987,43".reverse.chop.reverse
Chris Heald
que é um muito grande sobrecarga de desempenho para apenas uma tira de char
Pablo Fernandez
7
por que não usar [1 ..- 1] em vez de [1..self.length]?
horseyguy 01/09/10
O exemplo de conserto de macacos é bastante interessante para esta questão, é apenas IMO irrelevante e feio.
Dredezubov 17/03/2014
3

Agradecemos a @ the-tin-man por reunir os benchmarks!

Infelizmente, eu realmente não gosto de nenhuma dessas soluções. Eles exigem uma etapa extra para obter o resultado ( [0] = '', .strip!) ou não são muito semânticos / claros sobre o que está acontecendo ( [1..-1]: "Hum, um intervalo de 1 a 1 negativo? Anog?"), Ou são lentos ou longos para escreva ( .gsub, .length).

O que estamos tentando é uma 'mudança' (na linguagem Array), mas retornando os caracteres restantes, em vez do que foi mudado. Vamos usar nosso Ruby para tornar isso possível com strings! Podemos usar a operação rápida do suporte, mas dê um bom nome a ela e use um argumento para especificar quanto queremos extrair da frente:

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

Mas há mais que podemos fazer com essa operação de suporte rápido, mas difícil de manejar. Enquanto estamos nisso, para completar, vamos escrever um #shifte #firstpara String (por que Array deve ter toda a diversão‽‽), usando um argumento para especificar quantos caracteres queremos remover desde o início:

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, agora temos uma boa maneira clara de extrair caracteres da frente de uma string, com um método que seja consistente com Array#firste Array#shift(que realmente deve ser um método de estrondo ??). E também podemos obter facilmente a string modificada #eat!. Hum, devemos compartilhar nosso novo eat!poder com o Array? Por que não!

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

Agora podemos:

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

Isso é melhor!

brookr
fonte
2
Lembro-me de uma discussão anos atrás nos fóruns do Perl sobre essa função com o nome de em chip()vez de chop()(e chimp()como o análogo de chomp()).
Mark Thomas
2
str = "[12,23,987,43"

str[0] = ""
Artesão
fonte
7
É importante notar que isso só funcionará no Ruby 1.9. No Ruby 1.8, isso removerá o primeiro byte da string, não o primeiro caractere, que não é o que o OP deseja.
Jörg W Mittag
0
class String
  def bye_felicia()
    felicia = self.strip[0] #first char, not first space.
    self.sub(felicia, '')
  end
end
Josh Brody
fonte
0

Usando regex:

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

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

Acho uma boa solução str.delete(str[0])sua legibilidade, embora não possa atestar seu desempenho.

zeitchef
fonte
0

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

# => [2,3,4]

Lista elimina um ou mais elementos desde o início da matriz, não modifica a matriz e retorna a própria matriz em vez do elemento descartado.

Aaron Henderson
fonte