¿Cuál es la forma canónica de recortar una cadena en Ruby sin crear una nueva cadena?

182

Esto es lo que tengo ahora, que parece demasiado detallado para el trabajo que está haciendo.

@title        = tokens[Title].strip! || tokens[Title] if !tokens[Title].nil?

Asumir tokens es una matriz obtenida dividiendo una línea CSV. ahora las funciones como tira! chomp! et. todos devuelven nil si la cadena no se modificó

"abc".strip!    # => nil
" abc ".strip!  # => "abc"

¿Cuál es la forma en que Ruby dice recortar si contiene espacios iniciales o finales adicionales sin crear copias?

Se pone más feo si quiero hacer tokens[Title].chomp!.strip!

Gishu
fuente
3
Si va a leer cosas de tokens repetidamente, podría tener más sentido preprocesarlo. Es decir, "tokens.each {| t | t.strip!}". entonces puedes simplemente hacer "@title = tokens [Title] || ''"
glenn mcdonald

Respuestas:

272

Supongo que lo que quieres es:

@title = tokens[Title]
@title.strip!

El #strip!método devolverá nilsi no eliminó nada, y la variable en sí si se eliminó.

Según los estándares de Ruby, un método con un signo de exclamación cambia la variable en su lugar.

Espero que esto ayude.

Actualización: Este es el resultado de irbdemostrar:

>> @title = "abc"
=> "abc"
>> @title.strip!
=> nil
>> @title
=> "abc"
>> @title = " abc "
=> " abc "
>> @title.strip!
=> "abc"
>> @title
=> "abc"
Igor
fuente
1
Hmm ... todavía creo que @title = tokens [Title] .strip! se ve más limpio: es una pena que devuelva nulo en lugar de la cadena no modificada. A menos que alguien publique una respuesta mejor ... obtendrá la parte aceptada.
Gishu
77
Bueno, @title = tokens [Title] .strip hará el truco, pero en cambio tendrías una copia, lo cual está bien si mantienes la variable tokens [Title] intacta.
Igor
9
Ruby 1.9 tiene tap, que hace exactamente lo que quieres: @ title.tap {| x | x.strip!}
timkay
2
@timkay ¿Sería posible hacerlo @title.tap &:strip!? Eso parece más limpio que cualquier otra cosa.
Jon Egeland
16
¿Por qué en el mundo volvería a nilmenos que desnudara algo? Eso sin duda ha confundido a un montón de personas (ya que no tiene sentido).
Josh M.
53

Por cierto, ahora Ruby ya admite solo tirar sin "!".

Comparar:

p "abc".strip! == " abc ".strip!  # false, because "abc".strip! will return nil
p "abc".strip == " abc ".strip    # true

También es imposible stripsin duplicados. Ver fuentes en string.c:

static VALUE
rb_str_strip(VALUE str)
{
    str = rb_str_dup(str);
    rb_str_strip_bang(str);
    return str;
}

rubí 1.9.3p0 (30/10/2011) [i386-mingw32]

Actualización 1: como lo veo ahora, fue creado en el año 1999 (ver rev. 372 en SVN):

Actualización2: strip!no creará duplicados, tanto en las versiones 1.9.x, 2.xy troncales.

gaRex
fuente
1
"Es imposible despojar sin duplicados" - por supuesto que es posible, para eso strip!está.
Karoly Horvath
@KarolyHorvath ¿no ves el código fuente, escrito en C? Lea detenidamente lo que escribí aquí con respecto a los duplicados.
gaRex
1
Por supuesto que lo veo. Pero ese es el código fuente de strip. ¿Estoy malinterpretando algo? ¿De qué otra manera podría interpretar "imposible de quitar sin duplicar"?
Karoly Horvath
@KarolyHorvath siempre se crea un duplicado interno. Eso es sobre la cadena `str = rb_str_dup (str);`
gaRex
1
Y si no quieres un duplicado, usas strip!aka rb_str_strip_bang.
Karoly Horvath
9

No hay necesidad tanto de strip como de chomp ya que strip también eliminará los retornos del carro final, a menos que haya cambiado el separador de registros predeterminado y eso es lo que está masticando.

La respuesta de Olly ya tiene la forma canónica de hacer esto en Ruby, aunque si te encuentras haciendo esto mucho, siempre podrías definir un método para ello:

def strip_or_self!(str)
  str.strip! || str
end

Dando:

@title = strip_or_self!(tokens[Title]) if tokens[Title]

También tenga en cuenta que la instrucción if evitará que @titlese asigne si el token es nulo, lo que hará que mantenga su valor anterior. Si desea o no le importa que @titlesiempre se le asigne, puede mover el cheque al método y reducir aún más la duplicación:

def strip_or_self!(str)
  str.strip! || str if str
end

Como alternativa, si te sientes aventurero, puedes definir un método en String:

class String
  def strip_or_self!
    strip! || self
  end
end

Dando uno de:

@title = tokens[Title].strip_or_self! if tokens[Title]

@title = tokens[Title] && tokens[Title].strip_or_self!

fuente
9

Si estás usando Ruby on Rails hay un squish

> @title = " abc "
 => " abc " 

> @title.squish
 => "abc"
> @title
 => " abc "

> @title.squish!
 => "abc"
> @title
 => "abc" 

Si usa solo Ruby, quiere usar strip

Aquí yace el truco ... ¡en tu caso quieres usar strip sin el golpe!

mientras tira! Ciertamente devuelve nil si no hubo acción, todavía actualiza la variable, ¡así que tira! no se puede usar en línea. ¡Si quieres usar strip inline puedes usar la versión sin la explosión!

¡tira! utilizando un enfoque de varias líneas

> tokens["Title"] = " abc "
 => " abc "
> tokens["Title"].strip!
 => "abc"
> @title = tokens["Title"]
 => "abc"

tira enfoque de línea única ... SU RESPUESTA

> tokens["Title"] = " abc "
 => " abc "
> @title = tokens["Title"].strip if tokens["Title"].present?
 => "abc"
gateblues
fuente
4

Creo que su ejemplo es un enfoque sensato, aunque podría simplificarlo ligeramente como:

@title = tokens[Title].strip! || tokens[Title] if tokens[Title]

Alternativa, podría ponerlo en dos líneas:

@title = tokens[Title] || ''
@title.strip!
Olly
fuente
3

Si desea utilizar otro método después de necesitar algo como esto:

( str.strip || str ).split(',')

De esta manera, puedes desnudarte y seguir haciendo algo después :)

Diogo Neves - Mangaru
fuente
1

Mi manera:

> (@title = " abc ").strip!
 => "abc" 
> @title
 => "abc" 
Hauleth
fuente
1

Si tiene ruby ​​1.9 o soporte activo, puede hacerlo simplemente

@title = tokens[Title].try :tap, &:strip!

Esto es realmente genial, ya que aprovecha el :tryy el :tapmétodo, que son las construcciones funcionales más poderosas en ruby, en mi opinión.

Una forma aún más linda, pasando funciones como símbolos por completo:

@title = tokens[Title].send :try, :tap, &:strip!
reescrito
fuente
-1
@title = tokens[Title].strip! || tokens[Title]

Es completamente posible que no entienda el tema, pero ¿no haría esto lo que necesita?

" success ".strip! || "rescue" #=> "success"
"failure".strip! || "rescue" #=> "rescue"
Brent
fuente