Jaki jest kanoniczny sposób przycinania łańcucha w Ruby bez tworzenia nowego łańcucha?

182

Oto, co mam teraz - co wydaje się zbyt szczegółowe, by wykonywać swoją pracę.

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

Załóżmy, że tokeny to tablica uzyskana przez podzielenie linii CSV. teraz funkcje takie jak strip! chomp! et. wszystkie zwracają zero, jeśli ciąg nie został zmodyfikowany

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

Jak Ruby może powiedzieć „przyciąć”, jeśli zawiera dodatkowe spacje wiodące lub końcowe bez tworzenia kopii?

Staje się brzydszy, jeśli chcę tokens[Title].chomp!.strip!

Gishu
źródło
3
Jeśli zamierzasz wielokrotnie odczytywać elementy z tokenów, bardziej sensowne może być ich wstępne przetworzenie. Tj. „Tokens.each {| t | t.strip!}”. wtedy możesz po prostu zrobić "@title = tokeny [tytuł] || ''"
glenn mcdonald

Odpowiedzi:

272

Myślę, że chcesz:

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

#strip!Metoda zwróci nilgdyby nie pozbawić niczego, a sama zmienna, jeśli został pozbawiony.

Zgodnie ze standardami Ruby metoda opatrzona wykrzyknikiem zmienia zmienną na miejscu.

Mam nadzieję że to pomoże.

Aktualizacja: Dane wyjściowe służą irbdo zademonstrowania:

>> @title = "abc"
=> "abc"
>> @title.strip!
=> nil
>> @title
=> "abc"
>> @title = " abc "
=> " abc "
>> @title.strip!
=> "abc"
>> @title
=> "abc"
Igor
źródło
1
Hmm .. wciąż myślę, że @title = tokeny [Tytuł] .strip! wygląda na czystsze - szkoda, że ​​zwraca zero zamiast niemodyfikowanego ciągu. Chyba że ktoś opublikuje lepszą odpowiedź ... dostajesz zaakceptowany kawałek.
Gishu,
7
Cóż, @title = tokeny [Tytuł] .strip zrobi lewę, ale zamiast tego będziesz miał kopię, co jest w porządku, jeśli nie zmienisz zmiennej tokeny [Tytuł].
Igor
9
Ruby 1.9 ma tap, który robi dokładnie to, co chcesz: @ title.tap {| x | x.strip!}
timkay
2
@timkay Czy można to zrobić @title.tap &:strip!? To wydaje się czystsze niż cokolwiek innego.
Jon Egeland,
16
Dlaczego, u licha, wróci, niljeśli czegoś nie rozebra? To bez wątpienia pomieszało mnóstwo ludzi (ponieważ nie ma to sensu).
Josh M.
53

Btw, teraz ruby ​​obsługuje już tylko paski bez „!”.

Porównać:

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

Nie można też stripobejść się bez duplikatów. Zobacz źródła w 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]

Aktualizacja 1: Jak widzę teraz - została utworzona w 1999 roku (patrz rev 372 w SVN):

Aktualizacja 2: strip!nie utworzy duplikatów - zarówno w wersjach 1.9.x, 2.x, jak i trunk.

gaRex
źródło
1
„rozbiórki bez duplikatów nie można” - oczywiście jest to możliwe, po to strip!jest.
Karoly Horvath,
@KarolyHorvath nie widzisz kodu źródłowego napisanego w C? Proszę przeczytać uważnie, co tu napisałem o duplikatach.
gaRex
1
Oczywiście, że to widzę. Ale to jest kod źródłowy strip. Czy coś źle zrozumiałem? Jak inaczej mogę interpretować „niemożliwe do rozebrania bez duplikatu”?
Karoly Horvath
@KarolyHorvath duplikat wewnętrzny jest zawsze tworzony. To dotyczy ciągu `str = rb_str_dup (str);`
gaRex
1
A jeśli nie chcesz duplikatu, użyj strip!aka rb_str_strip_bang.
Karoly Horvath
9

Nie ma potrzeby zarówno stripowania, jak i chomp, ponieważ strip usunie również końcowe karetki - chyba że zmieniłeś domyślny separator rekordów i właśnie to chompresujesz.

Odpowiedź Olly ma już kanoniczny sposób na zrobienie tego w Ruby, chociaż jeśli często to robisz, zawsze możesz zdefiniować metodę:

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

Dający:

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

Należy również pamiętać, że instrukcja if nie pozwoli @titlena przypisanie, jeśli token ma wartość zero, co spowoduje zachowanie jej poprzedniej wartości. Jeśli chcesz lub nie przeszkadza ci @titlezawsze przydzielanie, możesz przenieść czek do metody i jeszcze bardziej ograniczyć powielanie:

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

Alternatywnie, jeśli masz ochotę na przygodę, możesz zdefiniować metodę na samym łańcuchu znaków:

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

Podanie jednego z:

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

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

źródło
9

Jeśli używasz Ruby on Rails, istnieje squish

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

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

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

Jeśli używasz tylko Ruby, chcesz użyć paska

Na tym polega gotcha .. w twoim przypadku chcesz użyć paska bez huku!

podczas gdy strip! z pewnością zwraca zero, jeśli nie było żadnej akcji, wciąż aktualizuje zmienną, więc rozbierz się! nie można używać w wierszu. Jeśli chcesz użyć paska inline, możesz użyć wersji bez huku!

rozebrać się! stosując podejście wieloliniowe

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

odejdź od linii pojedynczej ... ODPOWIEDŹ

> tokens["Title"] = " abc "
 => " abc "
> @title = tokens["Title"].strip if tokens["Title"].present?
 => "abc"
gateblues
źródło
4

Myślę, że twój przykład jest rozsądnym podejściem, chociaż możesz go nieco uprościć:

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

Alternatywnie możesz umieścić go w dwóch liniach:

@title = tokens[Title] || ''
@title.strip!
Olly
źródło
3

Jeśli chcesz użyć innej metody, gdy potrzebujesz czegoś takiego:

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

W ten sposób możesz rozebrać się i nadal coś robić po :)

Diogo Neves - Mangaru
źródło
1

Moja droga:

> (@title = " abc ").strip!
 => "abc" 
> @title
 => "abc" 
Hauleth
źródło
1

Jeśli masz wsparcie Ruby 1.9 lub aktywne, możesz to zrobić po prostu

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

To jest naprawdę fajne, jak to wykorzystuje :tryi :tapmetody, które są najpotężniejszym konstrukcje funkcjonalne rubin, moim zdaniem.

Nawet bardziej ładna forma, przekazująca funkcje jako symbole:

@title = tokens[Title].send :try, :tap, &:strip!
przepisane
źródło
-1
@title = tokens[Title].strip! || tokens[Title]

Jest całkiem możliwe, że nie rozumiem tematu, ale czy nie zrobiłbyś tego, czego potrzebujesz?

" success ".strip! || "rescue" #=> "success"
"failure".strip! || "rescue" #=> "rescue"
Brent
źródło