Ruby, ¿eliminar los últimos N caracteres de una cadena?

263

¿Cuál es la forma preferida de eliminar los últimos ncaracteres de una cadena?

JP Silvashy
fuente
Si conoce el sufijo (la respuesta principal sugiere mucho, venga a buscar esto), puede usar delete_suffixRuby 2.5. Más información aquí .
SRack

Respuestas:

36

Ruby 2.5+

A partir de Ruby 2.5, puede usar delete_suffixo delete_suffix!lograr esto de manera rápida y fácil de leer.

Los documentos sobre los métodos están aquí .

Si sabes cuál es el sufijo, esto es idiomático (y argumentaría, aún más legible que otras respuestas aquí):

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

Es incluso significativamente más rápido (casi 40% con el método de explosión) que la respuesta principal. Aquí está el resultado del mismo punto de referencia:

                     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)

Espero que esto sea útil: tenga en cuenta que el método no acepta actualmente una expresión regular, por lo que si no conoce el sufijo no es viable por el momento. Sin embargo, como la respuesta aceptada ( actualización: al momento de escribir ) dicta lo mismo, pensé que esto podría ser útil para algunas personas.

SRack
fuente
1
@JPSilvashy Nunca he estado tan contento de perder una marca de verificación. Esta es una respuesta genial.
Wayne Conrad
1
Esta es una función excelente y rápida, pero esta no es la respuesta correcta porque no responde la pregunta que es exactamenteWhat is the preferred way of removing the last n characters from a string?
Sam
1
Gracias por los comentarios @Sam: esta pregunta ha tenido una respuesta durante casi nueve años que funcionó igual que esta, con 261 votos al momento de escribir. JP aceptó eso y recientemente cambió a esto, por lo que diría que respondió a su pregunta :) Pensé en presentar esto como una alternativa moderna, y espero que ayude a las personas que aterrizan aquí. De hecho, he cubierto todo esto en la pregunta: espero que este método acepte una expresión regular pronto, aunque hay una alternativa perfecta para su caso justo debajo de esto.
SRack
316
irb> 'now is the time'[0...-4]
=> "now is the "
DigitalRoss
fuente
9
Tenga en cuenta que esto solo funciona en Ruby 1.9. En Ruby 1.8, esto eliminará los últimos bytes , no los últimos caracteres .
Jörg W Mittag
2
Pero probablemente quiso decir "bytes" si está usando 1.8.x.
DigitalRoss
44
Esto funciona, pero encuentro que 'abc123'.chomp (' 123 ') es el doble de rápido para cadenas cortas y muy largas. (Ruby 2.0.0-p247) ¿Alguien puede confirmar esto?
Plasmarob
10
Para aquellos que se preguntan por qué este ejemplo específico no funciona ... hay 3 puntos, no 2, es decir, [0...-4]no[0..-4]
rorofromfrance
2
@Plamarob Creo que es más relevante decir que chomp es 53 nanosegundos más rápido que decir que es el doble de rápido. Entonces puede estimar mejor el costo versus el valor de usarlo, y si podría ser lo que hay que optimizar en una situación dada.
cesoid
266

Si los caracteres que desea eliminar son siempre los mismos, considere chomp:

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

Las ventajas de chompson: sin contar, y el código comunica más claramente lo que está haciendo.

Sin argumentos, chompelimina el final de línea de DOS o Unix, si alguno está presente:

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

A partir de los comentarios, hubo una pregunta sobre la velocidad de usar #chompversus usar un rango. Aquí hay un punto de referencia que compara los dos:

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

Resultados de referencia (utilizando 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)

Entonces chomp es más rápido que usar un rango, en ~ 22%.

Wayne Conrad
fuente
55
Chop también es una opción. Es menos exigente con lo que se elimina.
Andrew Grimm el
1
Encontré que lo contrario es realmente cierto. La realidad es que .chomp parece ser constantemente el doble de rápido.
Plasmarob el
3
@Plamarob, los puntos de referencia en Ruby a menudo son sorprendentes. A menudo me equivoco que ya no intento adivinar qué será más rápido. Además, si la respuesta mejorara con los resultados de referencia, no dude en editarla.
Wayne Conrad el
1
Si estoy leyendo el código correctamente, el rango tarda aproximadamente 53 nanosegundos más que Chomp. Puede ser un lastre significativo en ciertas situaciones, pero tener en cuenta la velocidad absoluta (en lugar de solo la relativa) podría decirle si debe pasar por su base de código y cambiar todos sus rangos a chomps (cuando corresponda). Probablemente no.
cesoide
1
Funciona genial. Y puede usar chomp!()para actuar en la cadena en el lugar.
Joshua Pinter
57
str = str[0...-n]
Nakilon
fuente
55
Tenga en cuenta que esto solo funciona en Ruby 1.9. En Ruby 1.8, esto eliminará los últimos bytes , no los últimos caracteres .
Jörg W Mittag
55
Esto es mejor que la respuesta elegida, ya que 3 puntos (...) son más fáciles de memorizar ya que -n significa sacar n caracteres del final de la cadena.
lulalala
también "abcd" [0 ..- 2] # => "abc" mientras que "abcd" [0 ...- 2] # => "ab". En mi opinión, la opción de rango de 3 puntos resulta en un código más autoexplicativo.
mokagio
31

Yo sugeriría chop. Creo que se ha mencionado en uno de los comentarios pero sin enlaces ni explicaciones, así que he aquí por qué creo que es mejor:

Simplemente elimina el último carácter de una cadena y no tiene que especificar ningún valor para que eso suceda.

Si necesita eliminar más de un personaje, entonces chompes su mejor apuesta. Esto es lo que los documentos de Ruby tienen que decir sobre chop:

Devuelve una nueva cadena con el último carácter eliminado. Si la cadena termina con \ r \ n, ambos caracteres se eliminan. Aplicar chop a una cadena vacía devuelve una cadena vacía. String # chomp es a menudo una alternativa más segura, ya que deja la cadena sin cambios si no termina en un separador de registros.

Aunque esto se usa principalmente para eliminar separadores, como \r\nlo he usado para eliminar el último carácter de una cadena simple, por ejemplo, spara hacer que la palabra sea singular.

kakubei
fuente
1
¿No eliminaría eso solo ese último personaje? La pregunta es sobre "personajes" en plural
maetthew
1
Sí, tienes razón, pero chomp ('caracteres') eliminará los últimos 'caracteres'. No estaba claro para mí si el OP quería caracteres específicos o solo N caracteres.
kakubei
Sí, esta es la respuesta, solo cuando quieres eliminar solo un personaje (que fue el caso conmigo).
Quv
29
name = "my text"
x.times do name.chop! end

Aquí en la consola:

>name = "Nabucodonosor"
 => "Nabucodonosor" 
> 7.times do name.chop! end
 => 7 
> name
 => "Nabuco" 
joscas
fuente
¿Es esto eficiente? Parece que uno vale una comparación de referencia con otras respuestas :)
SRack
16

Dejar caer los últimos ncaracteres es lo mismo que conservar los primeros length - ncaracteres.

El Soporte activo incluye String#firsty String#lastmétodos que proporcionan una manera conveniente de mantener o soltar el primer / último ncarácter:

require 'active_support/core_ext/string/access'

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

Si está utilizando rieles, intente:

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

[EDITADO]

Para obtener la cadena SIN los últimos 2 caracteres:

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

Nota: la última cadena de caracteres está en n-1. Entonces, para eliminar los últimos 2, usamos n-3.

Guihen
fuente
2
Esto no elimina las dos últimas letras. Los devuelve.
JP Silvashy
1
No es necesario medir primero la cadena, ruby ​​usa índices negativos desde el final de la cadena de forma nativa
Devon Parsons
3

Siempre puedes usar algo como

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

¿Dónde Xestá el número de caracteres para eliminar?

O con la asignación / uso del resultado:

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

donde Xes el número de caracteres más uno para eliminar.

Zsolt Botykai
fuente
1

Si está de acuerdo con la creación de métodos de clase y desea los caracteres que corta, intente esto:

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'
Ben Aubin
fuente
0

Usando regex:

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

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

Si los caracteres que desea eliminar son siempre los mismos, considere chomp:

'abc123'. chomp ('123')

Poonkodi
fuente
-3
x = "my_test"
last_char = x.split('').last
Reshmi Mitra
fuente
3
la pregunta era sobre eliminar los últimos N caracteres de una cadena, no sobre encontrar el último carácter
unnitallman