Ruby, czy usunąć ostatnie N znaków z ciągu?

263

Jaki jest preferowany sposób usuwania ostatnich nznaków z ciągu?

JP Silvashy
źródło
Jeśli znasz sufiks (górna odpowiedź sugeruje, że dużo tu przychodzi i go szuka), możesz użyć delete_suffixRuby 2.5. Więcej informacji tutaj .
SRack

Odpowiedzi:

36

Ruby 2.5+

Jako Ruby 2.5 można wykorzystać delete_suffixalbo delete_suffix!do osiągnięcia tego celu w sposób szybki i czytelny sposób.

Dokumenty dotyczące metod są tutaj .

Jeśli wiesz, co to jest przyrostek, jest to idiomatyczne (i argumentowałbym, nawet bardziej czytelne niż inne odpowiedzi tutaj):

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

Jest nawet znacznie szybszy (prawie 40% w metodzie huku) niż najlepsza odpowiedź. Oto wynik tego samego testu porównawczego:

                     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)

Mam nadzieję, że jest to przydatne - zwróć uwagę, że metoda nie akceptuje obecnie wyrażenia regularnego, więc jeśli nie znasz sufiksu, na razie nie jest to wykonalne. Ponieważ jednak zaakceptowana odpowiedź ( aktualizacja: w momencie pisania ) narzuca to samo, pomyślałem, że może to być przydatne dla niektórych osób.

SRack
źródło
1
@JPSilvashy Nigdy nie byłem bardziej zadowolony z utraty znacznika wyboru. To świetna odpowiedź.
Wayne Conrad
1
To świetna i szybka funkcja, ale nie jest to właściwa odpowiedź, ponieważ nie odpowiada dokładnie na pytanieWhat is the preferred way of removing the last n characters from a string?
Sam
1
Dzięki za opinie @Sam - na to pytanie odpowiedź brzmiała przez prawie dziewięć lat, która działała tak samo jak ta, z 261 głosami pozytywnymi w momencie pisania. JP zaakceptował to i ostatnio przeszedł na to, więc powiedziałbym, że odpowiedział na ich pytanie :) Pomyślałem, że poddam to jako nowoczesną alternatywę i mam nadzieję, że pomoże to ludziom, którzy tu wylądują. W rzeczywistości omówiłem to wszystko w pytaniu - mam nadzieję, że ta metoda wkrótce zaakceptuje wyrażenie regularne, chociaż poniżej znajduje się idealna alternatywa dla twojego przypadku.
SRack
316
irb> 'now is the time'[0...-4]
=> "now is the "
DigitalRoss
źródło
9
Zauważ, że działa to tylko w Ruby 1.9. W Ruby 1.8 spowoduje to usunięcie ostatnich bajtów , a nie ostatnich znaków .
Jörg W Mittag
2
Ale prawdopodobnie miał na myśli „bajty”, jeśli używa 1.8.x.
DigitalRoss
4
To działa, ale uważam, że „abc123”. Chomp („123”) jest dwa razy szybszy zarówno dla krótkich, jak i bardzo długich łańcuchów. (Ruby 2.0.0-p247) Czy ktoś może to potwierdzić?
Plasmarob
10
Dla tych, którzy zastanawiają się, dlaczego ten konkretny przykład nie działa .. są 3 kropki, a nie 2, tj. [0...-4]Nie[0..-4]
rorofromfrance
2
@Plamarob Myślę, że bardziej istotne jest stwierdzenie, że chomp jest o 53 nanosekundy szybszy niż stwierdzenie, że jest dwa razy szybszy. Następnie możesz lepiej oszacować koszt w porównaniu do wartości korzystania z niego i czy warto zoptymalizować go w danej sytuacji.
cesoid
266

Jeśli znaki, które chcesz usunąć, są zawsze tymi samymi znakami, zastanów się chomp:

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

Zalety chomp: brak liczenia, a kod jaśniej komunikuje, co robi.

Bez argumentów chompusuwa końcówkę wiersza DOS lub Unix, jeśli występuje:

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

W komentarzach pojawiło się pytanie o szybkość używania w #chompporównaniu z użyciem zakresu. Oto test porównawczy dwóch:

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

Wyniki testu (przy użyciu 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)

Więc chomp jest szybszy niż użycie zakresu, o ~ 22%.

Wayne Conrad
źródło
5
Chop jest również opcją. To mniej wybredne, co zostanie usunięte.
Andrew Grimm,
1
Przekonałem się, że tak naprawdę jest odwrotnie. W rzeczywistości .chomp wydaje się być dwa razy szybszy.
Plasmarob
3
@Plamarob, Benchmarki w Ruby są często zaskakujące. Tak często się mylę, że nie próbuję już zgadywać, co będzie szybsze. Ponadto, jeśli odpowiedź zostanie poprawiona przez wyniki testu porównawczego, możesz ją edytować.
Wayne Conrad
1
Jeśli dobrze czytam kod, zasięg zajmuje około 53 nanosekund dłużej niż chomp. W niektórych sytuacjach może to być znaczny opór, ale pamiętanie o absolutnej prędkości (a nie tylko względnej) może powiedzieć ci, czy powinieneś przejść przez bazę kodu i zmienić wszystkie zakresy w chomps (jeśli dotyczy). Prawdopodobnie nie.
cesoid
1
Działa świetnie. I możesz użyć, chomp!()aby działać na ciąg w miejscu.
Joshua Pinter
57
str = str[0...-n]
Nakilon
źródło
5
Zauważ, że działa to tylko w Ruby 1.9. W Ruby 1.8 spowoduje to usunięcie ostatnich bajtów , a nie ostatnich znaków .
Jörg W Mittag
5
Jest to lepsze niż wybrana odpowiedź, ponieważ 3 kropki (...) są łatwiejsze do zapamiętania, ponieważ -n oznacza usunięcie n znaków z końca łańcucha.
lulalala,
również „abcd” [0 ..- 2] # => „abc”, podczas gdy „abcd” [0 ...- 2] # => „ab”. Moim zdaniem opcja zakresu 3 kropek daje bardziej zrozumiały kod.
mokagio
31

sugerowałbym chop . Myślę, że wspomniano o tym w jednym z komentarzy, ale bez linków ani wyjaśnień, dlatego uważam, że lepiej:

Usuwa po prostu ostatni znak z łańcucha i nie musisz określać żadnych wartości, aby tak się stało.

Jeśli chcesz usunąć więcej niż jedną postać, chompto najlepszy wybór. Oto, co mają do powiedzenia rubinowi doktorzychop :

Zwraca nowy ciąg z usuniętym ostatnim znakiem. Jeśli ciąg kończy się na \ r \ n, oba znaki są usuwane. Zastosowanie chop do pustego ciągu zwraca pusty ciąg. Ciąg # chomp jest często bezpieczniejszą alternatywą, ponieważ pozostawia ciąg bez zmian, jeśli nie kończy się separatorem rekordów.

Chociaż jest to używane głównie do usuwania separatorów, takich jak \r\nja użyłem go do usunięcia ostatniego znaku z prostego ciągu, na przykład, saby słowo było liczbą pojedynczą.

kakubei
źródło
1
Czy to nie po prostu usunie ten ostatni znak? Pytanie dotyczy „znaków” w liczbie mnogiej
maetthew
1
Tak, masz rację, ale chomp („znaki”) usunie ostatnie „znaki”. Nie było dla mnie jasne, czy OP chce konkretnych znaków, czy tylko N znaków.
kakubei
Tak, to jest odpowiedź, tylko wtedy, gdy chcesz usunąć tylko jedną postać (tak było w przypadku mnie).
Quv,
29
name = "my text"
x.times do name.chop! end

Tutaj w konsoli:

>name = "Nabucodonosor"
 => "Nabucodonosor" 
> 7.times do name.chop! end
 => 7 
> name
 => "Nabuco" 
joscas
źródło
Czy to jest wydajne? Wydaje się, że warto porównać wyniki z innymi odpowiedziami :)
SRack
16

Usunięcie ostatnich nznaków jest tym samym, co zachowanie pierwszych length - nznaków.

Aktywne wsparcie obejmuje String#firsti String#lastmetody, które zapewniają wygodny sposób na zachowanie lub upuszczenie pierwszych / ostatnich nznaków:

require 'active_support/core_ext/string/access'

"foobarbaz".first(3)  # => "foo"
"foobarbaz".first(-3) # => "foobar"
"foobarbaz".last(3)   # => "baz"
"foobarbaz".last(-3)  # => "barbaz"
Chocolateboy
źródło
7

jeśli używasz szyn, spróbuj:

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

[EDYTOWANE]

Aby uzyskać ciąg BEZ ostatnich 2 znaków:

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

Uwaga: ostatni ciąg znaków ma wartość n-1. Tak więc, aby usunąć ostatnie 2, używamy n-3.

Guihen
źródło
2
To nie usuwa ostatnich dwóch liter. Zwraca je.
JP Silvashy
1
Nie musisz najpierw mierzyć łańcucha, Ruby używa natywnie indeksów ujemnych z końca łańcucha
Devon Parsons,
3

Zawsze możesz użyć czegoś takiego

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

Gdzie Xjest liczba znaków do usunięcia.

Lub z przypisaniem / wykorzystaniem wyniku:

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

gdzie Xjest liczba znaków plus jeden do usunięcia.

Zsolt Botykai
źródło
1

Jeśli nie masz nic przeciwko tworzeniu metod klas i chcesz odciąć postacie, spróbuj tego:

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
źródło
0

Za pomocą wyrażenia regularnego:

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

str[/\A.{#{str.size-n}}/] #=> "stri"
Sagar Pandya
źródło
0

Jeśli znaki, które chcesz usunąć, są zawsze tymi samymi znakami, rozważ chomp:

„abc123”. chomp („123”)

Poonkodi
źródło
-3
x = "my_test"
last_char = x.split('').last
Reshmi Mitra
źródło
3
pytanie dotyczyło usunięcia ostatnich N znaków z ciągu, a nie znalezienia ostatniego znaku
unnitallman,