Qual é a maneira canônica de aparar uma string no Ruby sem criar uma nova string?

182

É isso que tenho agora - que parece muito detalhado para o trabalho que está sendo realizado.

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

Suponha que tokens é uma matriz obtida dividindo uma linha CSV. agora as funções como strip! chomp! et. todos retornam nulo se a sequência não foi modificada

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

Qual é a maneira do Ruby dizer recortá-lo se ele contiver espaços extras à esquerda ou à direita sem criar cópias?

Fica mais feio se eu quiser fazer tokens[Title].chomp!.strip!

Gishu
fonte
3
Se você estiver lendo coisas a partir de tokens repetidamente, pode fazer mais sentido pré-processá-lo. Ou seja, "tokens.each {| t | t.strip!}". então você pode simplesmente fazer "@title = tokens [Title] || ''"
glenn mcdonald

Respostas:

272

Eu acho que o que você quer é:

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

O #strip!método retornará nilse não remover nada e a própria variável se tiver sido removida.

De acordo com os padrões Ruby, um método com sufixo e um ponto de exclamação altera a variável no local.

Espero que isto ajude.

Atualização: Esta é a saída de irbpara demonstrar:

>> @title = "abc"
=> "abc"
>> @title.strip!
=> nil
>> @title
=> "abc"
>> @title = " abc "
=> " abc "
>> @title.strip!
=> "abc"
>> @title
=> "abc"
Igor
fonte
1
Hmm .. ainda acho que @title = tokens [Title] .strip! parece mais limpo - é uma pena que retorne nulo em vez da string não modificada. A menos que alguém publique uma resposta melhor .. você está recebendo a parte aceita.
Gishu
7
Bem, @title = tokens [Title] .strip fará o truque, mas você terá uma cópia, o que é bom se você mantiver a variável token [Title] intocada.
Igor
9
O Ruby 1.9 possui torneira, que faz exatamente o que você deseja: @ title.tap {| x | x.strip!}
timkay 6/06/11
2
@timkay Seria possível fazer isso @title.tap &:strip!? Isso parece mais limpo do que qualquer outra coisa.
21413 Jon Egeland
16
Por que no mundo retornaria a nilmenos que retirasse alguma coisa? Isso sem dúvida confundiu uma tonelada de pessoas (já que não faz sentido).
Josh
53

Aliás, agora o ruby ​​já suporta apenas strip sem "!".

Comparar:

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

Também é impossível stripsem duplicatas. Veja fontes em string.c:

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

ruby 1.9.3p0 (30-10-2011) [i386-mingw32]

Atualização 1: Como eu vejo agora - ela foi criada em 1999 (veja a revisão nº 372 no SVN):

Atualização 2: strip!não criará duplicatas - nas versões 1.9.x, 2.xe tronco.

gaRex
fonte
1
"é impossível remover sem duplicatas" - é claro que é possível, strip!é para isso.
Karoly Horvath
@KarolyHorvath 'você vê o código fonte, escrito em C? Pls leia atentamente, o que escrevi aqui sobre duplicatas.
gaRex
1
Claro que eu vejo. Mas esse é o código fonte do strip. Estou entendendo mal alguma coisa? De que outra forma eu poderia interpretar "impossível remover sem duplicar"?
309 Karoly Horvath
O @KarolyHorvath duplicado internamente é sempre criado. Isso é sobre string `str = rb_str_dup (str);`
gaRex
1
E se você não quiser uma duplicata, use o strip!aka rb_str_strip_bang.
309 Karoly Horvath
9

Não há necessidade de remover e chomp, pois a tira também remove os retornos de carro à direita - a menos que você tenha alterado o separador de registros padrão e é isso que você está fazendo.

A resposta de Olly já tem a maneira canônica de fazer isso em Ruby, mas se você se encontra fazendo muito isso, pode sempre definir um método para isso:

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

Dando:

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

Lembre-se também de que a instrução if impedirá @titlede ser atribuída se o token for nulo, o que resultará na manutenção do valor anterior. Se você deseja ou não se importa de @titlesempre ser designado, pode mover a verificação para o método e reduzir ainda mais a duplicação:

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

Como alternativa, se você estiver se sentindo aventureiro, poderá definir um método no próprio String:

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

Dando um dos seguintes:

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

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

fonte
9

Se você estiver usando Ruby on Rails, há um squish

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

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

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

Se você estiver usando apenas Ruby, você quer usar strip

Aqui está o truque .. no seu caso, você deseja usar a tira sem o estrondo!

enquanto tira! certamente retorna nulo se não houver ação, ele ainda atualiza a variável, então tira! não pode ser usado em linha. Se você deseja usar a tira em linha, pode usar a versão sem o estrondo!

faixa! usando abordagem de várias linhas

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

abordagem de linha única de tira ... SUA RESPOSTA

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

Eu acho que seu exemplo é uma abordagem sensata, embora você possa simplificá-lo um pouco como:

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

Alternativa, você pode colocá-lo em duas linhas:

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

Se você quiser usar outro método depois de precisar de algo como isto:

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

Desta forma, você pode retirar e ainda fazer alguma coisa depois :)

Diogo Neves - Mangaru
fonte
1

O meu caminho:

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

Se você possui o ruby ​​1.9 ou ativa o suporte, pode fazer simplesmente

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

Isso é muito legal, pois aproveita :trye:tap método, que são os mais poderosos construções funcionais no rubi, na minha opinião.

Uma forma ainda mais bonita, a passagem funciona como símbolos:

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

É perfeitamente possível que eu não esteja entendendo o tópico, mas isso não faria o que você precisa?

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