Ruby, supprimer les N derniers caractères d'une chaîne?


263

Quelle est la meilleure façon de supprimer les derniers ncaractères d'une chaîne?


Si vous connaissez le suffixe (la première réponse suggère que beaucoup viennent ici à la recherche de cela), vous pouvez utiliser à delete_suffixpartir de Ruby 2.5. Plus d'infos ici .
SRack

Réponses:


36

Ruby 2.5+

Depuis Ruby 2.5, vous pouvez utiliser delete_suffixou delete_suffix!pour y parvenir de manière rapide et lisible.

Les documents sur les méthodes sont ici .

Si vous savez quel est le suffixe, c'est idiomatique (et je dirais, encore plus lisible que les autres réponses ici):

'abc123'.delete_suffix('123')     # => "abc"
'abc123'.delete_suffix!('123')    # => "abc"

C'est encore beaucoup plus rapide (près de 40% avec la méthode du bang) que la meilleure réponse. Voici le résultat du même benchmark:

                     user     system      total        real
chomp            0.949823   0.001025   0.950848 (  0.951941)
range            1.874237   0.001472   1.875709 (  1.876820)
delete_suffix    0.721699   0.000945   0.722644 (  0.723410)
delete_suffix!   0.650042   0.000714   0.650756 (  0.651332)

J'espère que cela est utile - notez que la méthode n'accepte pas actuellement une expression régulière, donc si vous ne connaissez pas le suffixe, il n'est pas viable pour le moment. Cependant, comme la réponse acceptée ( mise à jour: au moment de la rédaction ) l'impose, j'ai pensé que cela pourrait être utile à certaines personnes.


1
@JPSilvashy Je n'ai jamais été aussi heureux de perdre une coche. C'est une excellente réponse.
Wayne Conrad

1
C'est une fonction formidable et rapide, mais ce n'est pas la bonne réponse car elle ne répond pas exactement à la questionWhat is the preferred way of removing the last n characters from a string?
Sam

1
Merci pour la rétroaction @Sam - cette question a eu une réponse pendant près de neuf ans qui a fonctionné de la même manière que celle-ci, avec 261 votes positifs au moment de la rédaction. JP a accepté cela et est récemment passé à cela, alors je dirais que cela a répondu à leur question :) Je pensais que j'introduirais cela comme une alternative moderne, et j'espère que cela aidera les gens qui atterrissent ici. En fait, j'ai couvert tout cela dans la question - j'espère que cette méthode acceptera bientôt une expression régulière, bien qu'il existe une alternative parfaite pour votre cas juste en dessous.
SRack

316
irb> 'now is the time'[0...-4]
=> "now is the "

9
Notez que cela ne fonctionne que dans Ruby 1.9. Dans Ruby 1.8, cela supprimera les derniers octets , pas les derniers caractères .
Jörg W Mittag

2
Mais il voulait probablement dire "octets" s'il utilise 1.8.x.
DigitalRoss

4
Cela fonctionne, mais je trouve que 'abc123'.chomp (' 123 ') est deux fois plus rapide pour les cordes courtes et très longues. (Ruby 2.0.0-p247) Quelqu'un peut-il confirmer cela?
Plasmarob

10
Pour ceux qui se demandent pourquoi cet exemple spécifique ne fonctionne pas ... il y a 3 points et non 2 c'est-à-dire [0...-4]pas[0..-4]
rorofromfrance

2
@Plamarob Je pense qu'il est plus pertinent de dire que le chomp est 53 nanosecondes plus rapide que de dire qu'il est deux fois plus rapide. Ensuite, vous pouvez mieux estimer le coût par rapport à la valeur de son utilisation, et déterminer si ce serait la chose à optimiser dans une situation donnée.
Cesoid

266

Si les caractères que vous souhaitez supprimer sont toujours les mêmes, envisagez chomp:

'abc123'.chomp('123')    # => "abc"

Les avantages de chompsont: pas de comptage, et le code communique plus clairement ce qu'il fait.

Sans aucun argument, chompsupprime la fin de ligne DOS ou Unix, si l'un ou l'autre est présent:

"abc\n".chomp      # => "abc"
"abc\r\n".chomp    # => "abc"

D'après les commentaires, il y avait une question de la vitesse d'utilisation #chomppar rapport à l'utilisation d'une plage. Voici une référence comparant les deux:

require 'benchmark'

S = 'asdfghjkl'
SL = S.length
T = 10_000
A = 1_000.times.map { |n| "#{n}#{S}" }

GC.disable

Benchmark.bmbm do |x|
  x.report('chomp') { T.times { A.each { |s| s.chomp(S) } } }
  x.report('range') { T.times { A.each { |s| s[0...-SL] } } }
end

Résultats de référence (en utilisant CRuby 2.13p242):

Rehearsal -----------------------------------------
chomp   1.540000   0.040000   1.580000 (  1.587908)
range   1.810000   0.200000   2.010000 (  2.011846)
-------------------------------- total: 3.590000sec

            user     system      total        real
chomp   1.550000   0.070000   1.620000 (  1.610362)
range   1.970000   0.170000   2.140000 (  2.146682)

Le chomp est donc plus rapide que l'utilisation d'une plage, de ~ 22%.


5
Chop est également une option. Il est moins difficile de savoir ce qui est supprimé.
Andrew Grimm

1
J'ai trouvé que le contraire est vrai. La réalité est que .chomp semble toujours être deux fois plus rapide.
Plasmarob

3
@Plamarob, Les benchmarks en Ruby sont souvent surprenants. Je me trompe si souvent que je n'essaye plus de deviner ce qui sera plus rapide. De plus, si la réponse serait améliorée par les résultats du benchmark, n'hésitez pas à la modifier.
Wayne Conrad

1
Si je lis bien le code, la plage prend environ 53 nanosecondes de plus que chomp. Cela peut être un frein important dans certaines situations, mais en gardant à l'esprit la vitesse absolue (plutôt que simplement la relative), vous pourriez savoir si vous devez parcourir votre base de code et changer toutes vos plages en chomps (le cas échéant). Probablement pas.
Cesoid

1
Fonctionne très bien. Et vous pouvez utiliser chomp!()pour agir sur la chaîne en place.
Joshua Pinter

57
str = str[0...-n]

5
Notez que cela ne fonctionne que dans Ruby 1.9. Dans Ruby 1.8, cela supprimera les derniers octets , pas les derniers caractères .
Jörg W Mittag

5
C'est mieux que la réponse choisie, car 3 points (...) sont plus faciles à mémoriser car -n signifie retirer n caractères de la fin de la chaîne.
lulalala

également "abcd" [0 ..- 2] # => "abc" tandis que "abcd" [0 ...- 2] # => "ab". À mon avis, l'option de plage de 3 points donne un code plus explicite.
mokagio

31

Je suggérerais chop. Je pense que cela a été mentionné dans l'un des commentaires mais sans liens ni explications alors voici pourquoi je pense que c'est mieux:

Il supprime simplement le dernier caractère d'une chaîne et vous n'avez pas besoin de spécifier de valeurs pour que cela se produise.

Si vous devez supprimer plusieurs personnages, chompc'est votre meilleur pari. Voici ce que les docs rubis ont à dire sur chop:

Renvoie une nouvelle chaîne avec le dernier caractère supprimé. Si la chaîne se termine par \ r \ n, les deux caractères sont supprimés. L'application de hacher à une chaîne vide renvoie une chaîne vide. La chaîne # chomp est souvent une alternative plus sûre, car elle laisse la chaîne inchangée si elle ne se termine pas dans un séparateur d'enregistrement.

Bien que cela soit principalement utilisé pour supprimer des séparateurs tels que \r\nje l'ai utilisé pour supprimer le dernier caractère d'une chaîne simple, par exemple spour rendre le mot singulier.


1
Cela ne supprimerait-il pas simplement ce dernier caractère? La question concerne les "personnages" au pluriel
maetthew

1
Oui, vous avez raison, mais chomp ('chars') supprimera les derniers 'chars'. Il n'était pas clair pour moi si l'OP voulait des caractères spécifiques ou seulement N caractères.
kakubei

Oui, c'est la réponse, uniquement lorsque vous souhaitez supprimer un seul caractère (ce qui était le cas pour moi cependant).
Quv

29
name = "my text"
x.times do name.chop! end

Ici dans la console:

>name = "Nabucodonosor"
 => "Nabucodonosor" 
> 7.times do name.chop! end
 => 7 
> name
 => "Nabuco" 

Est-ce efficace? Semble une valeur une comparaison de référence à d'autres réponses :)
SRack

16

La suppression des derniers ncaractères revient à conserver les premiers length - ncaractères.

Le soutien actif comprend String#firstet des String#lastméthodes qui fournissent un moyen pratique de conserver ou de laisser tomber les premiers / derniers ncaractères:

require 'active_support/core_ext/string/access'

"foobarbaz".first(3)  # => "foo"
"foobarbaz".first(-3) # => "foobar"
"foobarbaz".last(3)   # => "baz"
"foobarbaz".last(-3)  # => "barbaz"

7

si vous utilisez des rails, essayez:

"my_string".last(2) # => "ng"

[ÉDITÉ]

Pour obtenir la chaîne SANS les 2 derniers caractères:

n = "my_string".size
"my_string"[0..n-3] # => "my_stri"

Remarque: le dernier caractère de chaîne est à n-1. Donc, pour supprimer les 2 derniers, nous utilisons n-3.


2
Cela ne supprime pas les deux dernières lettres. Il les renvoie.
JP Silvashy

1
Vous n'avez pas besoin de mesurer la chaîne en premier, ruby ​​utilise nativement les index négatifs de la fin de la chaîne
Devon Parsons

3

Vous pouvez toujours utiliser quelque chose comme

 "string".sub!(/.{X}$/,'')

Xest le nombre de caractères à supprimer.

Ou en affectant / utilisant le résultat:

myvar = "string"[0..-X]

Xest le nombre de caractères plus un à supprimer.



1

Si vous êtes d'accord avec la création de méthodes de classe et voulez les caractères que vous coupez, essayez ceci:

class String
  def chop_multiple(amount)
    amount.times.inject([self, '']){ |(s, r)| [s.chop, r.prepend(s[-1])] }
  end
end

hello, world = "hello world".chop_multiple 5
hello #=> 'hello '
world #=> 'world'

0

Utilisation de regex:

str = 'string'
n = 2  #to remove last n characters

str[/\A.{#{str.size-n}}/] #=> "stri"

0

Si les caractères que vous souhaitez supprimer sont toujours les mêmes, alors envisagez chomp:

«abc123». chomp ('123')


-3
x = "my_test"
last_char = x.split('').last

3
la question portait sur la suppression des N derniers caractères d'une chaîne, et non sur la recherche du dernier caractère
unlitallman
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.