Quelle est la manière la plus simple de supprimer le premier caractère d'une chaîne?


174

Exemple:

[12,23,987,43

Quel est le moyen le plus rapide et le plus efficace de supprimer le " [", en utilisant peut-être un chop()mais pour le premier caractère?


1
J'ai modifié ma réponse, il est donc possible de modifier la réponse sélectionnée. Voyez si vous pouvez l'attribuer à la réponse de Jason Stirk puisque la sienne est la plus rapide et est très lisible.
the Tin Man

3
Utilisez str [1 ..- 1], son plus rapide selon les réponses ci-dessous.
Achyut Rastogi le

1
À partir de Ruby 2.5, vous pouvez utiliser delete_prefixet delete_prefix!- plus de détails ci-dessous . Je n'ai pas eu le temps de faire des benchmarks, mais je le ferai bientôt!
SRack

Mise à jour: j'ai comparé les nouvelles méthodes ( delete_prefix\ delete_prefix!) et elles sont assez rapides. Ce n'est pas tout à fait le cas des favoris précédents pour la vitesse, mais la lisibilité signifie que ce sont de nouvelles options intéressantes à avoir!
SRack

Réponses:


233

Je préfère utiliser quelque chose comme:

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

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

Je suis toujours à la recherche de la manière la plus rapide et la plus lisible de faire les choses:

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

Exécution sur mon 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)

Mise à jour pour incorporer une autre réponse suggérée:

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

Ce qui se traduit par:

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)

Et un autre utilisant /^./pour trouver le premier caractère:

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

Ce qui se traduit par:

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

Voici une autre mise à jour sur un matériel plus rapide et une version plus récente de 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)

Pourquoi gsub est-il si lent?

Après avoir effectué une recherche / remplacement, gsubdoit vérifier d'éventuelles correspondances supplémentaires avant de pouvoir dire si c'est terminé. subn'en fait qu'un et se termine. Considérez gsubque c'est un minimum de deux subappels.

En outre, il est important de se souvenir de cela gsub, et subpeut également être handicapé par une expression régulière mal écrite qui correspond beaucoup plus lentement qu'une recherche de sous-chaîne. Si possible, ancrez l'expression régulière pour en tirer le maximum de vitesse. Il y a des réponses ici sur Stack Overflow démontrant cela, alors cherchez si vous voulez plus d'informations.


34
Il est important de noter que cela ne fonctionnera que dans Ruby 1.9. Dans Ruby 1.8, cela supprimera le premier octet de la chaîne, pas le premier caractère, ce qui n'est pas ce que veut l'OP.
Jörg W Mittag

+1: J'oublie toujours qu'à une position de chaîne, vous pouvez attribuer non seulement un seul caractère, mais aussi insérer une sous-chaîne. Merci!
quetzalcoatl

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

4
Cela le supprime de toutes les positions, ce qui n'est pas ce que voulait l'OP: "... pour le premier caractère?".
the Tin Man

2
" what about "[12,23,987,43".shift ?"? Qu'en est-il de "[12,23,987,43".shift NoMethodError: undefined method shift 'pour "[12,23,987,43": String`?
the Tin Man

293

Similaire à la réponse de Pablo ci-dessus, mais un nettoyeur d'ombre:

str[1..-1]

Renvoie le tableau de 1 au dernier caractère.

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

13
+1 Jetez un œil aux résultats de référence que j'ai ajoutés à ma réponse. Vous avez le temps d'exécution le plus rapide, et je pense que c'est très propre.
the Tin Man

Qu'en est-il des performances de str[1,]par rapport à celles ci-dessus?
Bohr

1
@Bohr: str[1,]vous renvoie le 2ème caractère puisque la plage est 1:nil. Vous devez fournir la longueur calculée réelle, ou quelque chose qui est garanti supérieur à length, comme, str[1,999999](utilisez int_max bien sûr) pour obtenir la queue entière. [1..-1]est plus propre et probablement plus rapide, car vous n'avez pas besoin d'opérer manuellement sur la longueur (voir [1..length] dans le benchmark)
quetzalcoatl

4
Très belle solution. Au fait si l'on veut supprimer les premier et dernier caractères:str[1..-2]
pisaruk

50

Nous pouvons utiliser slice pour faire ceci:

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

En utilisant, slice!nous pouvons supprimer n'importe quel caractère en spécifiant son index.


2
Cet élégant slice!(0)devrait vraiment être la réponse choisie, car utiliser asdf[0] = '' pour supprimer le premier caractère est ridicule (tout comme utiliser gsub avec regex et tirer une mouche avec un obusier).
f055

1
Bien que cela puisse sembler peu intuitif en surface, []=cela ne nécessite pas autant de code C sous-jacent, ce qui slice!nécessite un travail supplémentaire. Cela ajoute. L'argument pourrait être "Lequel est le plus lisible?" Je trouve l'utilisation []=lisible, mais je viens d'un fond C -> Perl qui colore probablement ma pensée. Les développeurs Java penseraient probablement que c'est moins lisible. L'un ou l'autre est un moyen acceptable d'accomplir la tâche tant qu'elle est facilement compréhensible et maintenable et qu'elle ne charge pas indûment le processeur.
the Tin Man

D'accord. Savez-vous comment nous pouvons mesurer si une fonction prend beaucoup de charge CPU dans ROR? ou devrions-nous utiliser la différence de temps d'exécution en milli ou nanosecondes?
balanv

18

Ruby 2.5+

Depuis Ruby 2.5, vous pouvez utiliser delete_prefixou delete_prefix!pour y parvenir de manière lisible.

Dans ce cas "[12,23,987,43".delete_prefix("[").

Plus d'infos ici:

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

NB vous pouvez également l'utiliser pour supprimer des éléments de la fin d'une chaîne avec delete_suffix etdelete_suffix!

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

Éditer:

En utilisant la configuration de référence de Tin Man, cela semble assez rapide aussi (sous les deux dernières entrées delete_pet delete_p!). Ne fait pas tout à fait pipi les favoris précédents pour la vitesse, bien qu'il soit très lisible.

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

Si vous souhaitez toujours supprimer les crochets de début:

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

Si vous souhaitez simplement supprimer le premier caractère et que vous savez qu'il ne sera pas dans un jeu de caractères multi-octets:

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

ou

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

1
J'utiliserais à la "[12,23,987,43".sub(/^\[+/, "")place de gsub(/^\[/, ""). Le premier permet au moteur de regex de trouver toutes les correspondances, puis ils sont remplacés en une seule action et entraînent une amélioration de la vitesse d'environ 2x avec Ruby 1.9.3.
the Tin Man

1
Puisque nous avons affaire à des chaînes, est-ce que cela devrait être gsub(/\A\[/, "") ?
Sagar Pandya

6

Alternative inefficace:

str.reverse.chop.reverse

4

Par exemple: 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" 

De cette façon, vous pouvez supprimer un par un premier caractère de la chaîne.


3
C'est la même chose que la réponse de Jason Stirk, seule la sienne a été soumise plusieurs mois auparavant.
the Tin Man

3

Moyen facile:

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

removed = str[1..str.length]

Manière géniale:

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

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

(Remarque: préférez le moyen facile :))


1
Si vous voulez conserver la sémantique "chop", vous pouvez simplement"[12,23,987,43".reverse.chop.reverse
Chris Heald

c'est une assez grosse surcharge de performance juste pour dépouiller un caractère
Pablo Fernandez

7
pourquoi ne pas utiliser [1 ..- 1] plutôt que [1..self.length]?
horseyguy

L'exemple de correctif de singe est assez faux pour cette question, il est juste hors de propos et laid IMO.
dredozubov

3

Merci à @ the-tin-man pour avoir rassemblé les benchmarks!

Hélas, je n'aime vraiment aucune de ces solutions. Soit ils nécessitent une étape supplémentaire pour obtenir le résultat ( [0] = '', .strip!), soit ils ne sont pas très sémantiques / clairs sur ce qui se passe ( [1..-1]: "Um, une plage de 1 à moins 1? Yearg?"), Ou ils sont lents ou longs à écrire ( .gsub, .length).

Ce que nous essayons est un «décalage» (dans le langage Array), mais renvoyant les caractères restants, plutôt que ce qui a été décalé. Utilisons notre Ruby pour rendre cela possible avec des chaînes! Nous pouvons utiliser l'opération de support rapide, mais donnez-lui un bon nom et prenez un argument pour spécifier à quel point nous voulons grignoter le front:

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

Mais nous pouvons faire plus avec cette opération de support rapide mais peu maniable. Pendant que nous y sommes, pour être complet, écrivons un #shiftet #firstpour String (pourquoi Array devrait-il avoir tout le plaisir?), En prenant un argument pour spécifier le nombre de caractères que nous voulons supprimer depuis le début:

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, maintenant nous avons un bon moyen clair de retirer des caractères au début d'une chaîne, avec une méthode cohérente avec Array#firstet Array#shift(qui devrait vraiment être une méthode bang ??). Et nous pouvons facilement obtenir la chaîne modifiée avec #eat!. Hum, devrions-nous partager notre nouvelle eat!puissance avec Array? Pourquoi pas!

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

Maintenant nous pouvons:

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

C'est mieux!


2
Je me souviens d'une discussion il y a des années dans les forums Perl à propos d'une telle fonction avec le nom de chip()au lieu de chop()(et chimp()comme l'analogue de chomp()).
Mark Thomas

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

str[0] = ""

7
Il est important de noter que cela ne fonctionnera que dans Ruby 1.9. Dans Ruby 1.8, cela supprimera le premier octet de la chaîne, pas le premier caractère, ce qui n'est pas ce que veut l'OP.
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

Utilisation de regex:

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

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

0

Je trouve une bonne solution str.delete(str[0])pour sa lisibilité, même si je ne peux pas attester de ses performances.


0

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

# => [2,3,4]

List supprime un ou plusieurs éléments depuis le début du tableau, ne mute pas le tableau et renvoie le tableau lui-même au lieu de l'élément déposé.

En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.