Ruby, letzte N Zeichen aus einer Zeichenfolge entfernen?

263

Was ist die bevorzugte Methode zum Entfernen der letzten nZeichen aus einer Zeichenfolge?

JP Silvashy
quelle
Wenn Sie das Suffix kennen (die oberste Antwort deutet darauf hin, dass hier viel gesucht wird), können Sie delete_suffixRuby 2.5 verwenden. Mehr Infos hier .
SRack

Antworten:

36

Ruby 2.5+

Ab Ruby 2.5 können Sie dies schnell und lesbar verwenden delete_suffixoder delete_suffix!erreichen.

Die Dokumente zu den Methoden finden Sie hier .

Wenn Sie wissen, was das Suffix ist, ist dies idiomatisch (und ich würde argumentieren, noch besser lesbar als andere Antworten hier):

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

Es ist sogar deutlich schneller (fast 40% mit der Bang-Methode) als die Top-Antwort. Hier ist das Ergebnis des gleichen Benchmarks:

                     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)

Ich hoffe, dies ist nützlich - beachten Sie, dass die Methode derzeit keinen regulären Ausdruck akzeptiert. Wenn Sie das Suffix nicht kennen, ist es vorerst nicht realisierbar. Da die akzeptierte Antwort ( Update: zum Zeitpunkt des Schreibens ) dasselbe vorschreibt, dachte ich, dass dies für einige Leute nützlich sein könnte.

SRack
quelle
1
@JPSilvashy Ich war noch nie so erfreut, ein Häkchen zu verlieren. Dies ist eine großartige Antwort.
Wayne Conrad
1
Dies ist eine großartige und schnelle Funktion, aber dies ist nicht die richtige Antwort, da sie nicht genau die Frage beantwortetWhat is the preferred way of removing the last n characters from a string?
Sam
1
Vielen Dank für das Feedback @Sam - diese Frage hatte fast neun Jahre lang eine Antwort, die genauso funktionierte wie diese, mit 261 positiven Stimmen zum Zeitpunkt des Schreibens. JP hat das akzeptiert und kürzlich darauf umgestellt, also würde ich sagen, dass es ihre Frage beantwortet hat :) Ich dachte, ich würde dies als moderne Alternative einfügen und hoffe, dass es Menschen hilft, die hier landen. Tatsächlich habe ich all dies in der Frage behandelt - ich hoffe, dass diese Methode bald einen regulären Ausdruck akzeptiert, obwohl es eine perfekte Alternative für Ihren Fall direkt darunter gibt.
SRack
316
irb> 'now is the time'[0...-4]
=> "now is the "
DigitalRoss
quelle
9
Beachten Sie, dass dies nur in Ruby 1.9 funktioniert. In Ruby 1.8 werden dadurch die letzten Bytes entfernt , nicht die letzten Zeichen .
Jörg W Mittag
2
Aber er meinte wahrscheinlich "Bytes", wenn er 1.8.x verwendet.
DigitalRoss
4
Das funktioniert, aber ich finde, dass 'abc123'.chomp (' 123 ') sowohl für kurze als auch für sehr lange Saiten doppelt so schnell ist. (Ruby 2.0.0-p247) Kann jemand dies bestätigen?
Plasmarob
10
Für diejenigen, die sich fragen, warum dieses spezielle Beispiel nicht funktioniert. Es gibt 3 Punkte, nicht 2, dh [0...-4]nicht[0..-4]
rorofromfrance
2
@Plamarob Ich denke, es ist relevanter zu sagen, dass Chomp 53 Nanosekunden schneller ist als zu sagen, dass es doppelt so schnell ist. Dann können Sie die Kosten im Vergleich zum Wert der Verwendung besser abschätzen und feststellen, ob in einer bestimmten Situation eine Optimierung erforderlich ist.
Cesoid
266

Wenn die Zeichen, die Sie entfernen möchten, immer dieselben Zeichen sind, beachten Sie Folgendes chomp:

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

Die Vorteile von chomp sind: kein Zählen, und der Code kommuniziert klarer, was er tut.

Entfernt ohne Argumente chompdas DOS- oder Unix-Zeilenende, falls eines der beiden vorhanden ist:

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

Aus den Kommentaren ging die Frage nach der Geschwindigkeit der Verwendung im #chompVergleich zur Verwendung eines Bereichs hervor. Hier ist ein Benchmark, der die beiden vergleicht:

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

Benchmark-Ergebnisse (mit 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)

Chomp ist also um ~ 22% schneller als die Verwendung eines Bereichs.

Wayne Conrad
quelle
5
Hacken ist auch eine Option. Es ist weniger pingelig, was entfernt wird.
Andrew Grimm
1
Ich fand das Gegenteil tatsächlich wahr. Die Realität ist, dass .chomp durchweg doppelt so schnell zu sein scheint.
Plasmarob
3
@Plamarob, Benchmarks in Ruby sind oft überraschend. Ich irre mich so oft, dass ich nicht mehr versuche zu erraten, was schneller sein wird. Wenn die Antwort durch die Benchmark-Ergebnisse verbessert würde, können Sie sie gerne bearbeiten.
Wayne Conrad
1
Wenn ich den Code richtig lese, dauert die Reichweite etwa 53 Nanosekunden länger als die von chomp. In bestimmten Situationen kann dies eine erhebliche Belastung darstellen. Wenn Sie jedoch die absolute Geschwindigkeit (und nicht nur die relative Geschwindigkeit) berücksichtigen, können Sie möglicherweise feststellen, ob Sie Ihre Codebasis durchgehen und alle Bereiche in Chomps ändern sollten (sofern zutreffend). Wahrscheinlich nicht.
Cesoid
1
Funktioniert super. Und Sie können chomp!()damit direkt auf den String einwirken.
Joshua Pinter
57
str = str[0...-n]
Nakilon
quelle
5
Beachten Sie, dass dies nur in Ruby 1.9 funktioniert. In Ruby 1.8 werden dadurch die letzten Bytes entfernt , nicht die letzten Zeichen .
Jörg W Mittag
5
Dies ist besser als die gewählte Antwort, da 3 Punkte (...) leichter zu merken sind, da -n bedeutet, dass n Zeichen am Ende der Zeichenfolge entfernt werden.
Lulalala
auch "abcd" [0 ..- 2] # => "abc" während "abcd" [0 ...- 2] # => "ab". Meiner Meinung nach führt die Option mit 3 Punkten zu einem selbsterklärenderen Code.
Mokagio
31

ich würde vorschlagen chop . Ich denke, es wurde in einem der Kommentare erwähnt, aber ohne Links oder Erklärungen. Deshalb denke ich, dass es besser ist:

Es wird einfach das letzte Zeichen aus einer Zeichenfolge entfernt, und Sie müssen dafür keine Werte angeben.

Wenn Sie mehr als einen Charakter entfernen müssen, chompist dies die beste Wahl. Dies ist, was die Ruby-Dokumente zu sagen haben chop:

Gibt einen neuen String zurück, bei dem das letzte Zeichen entfernt wurde. Wenn die Zeichenfolge mit \ r \ n endet, werden beide Zeichen entfernt. Wenn Sie chop auf eine leere Zeichenfolge anwenden, wird eine leere Zeichenfolge zurückgegeben. String # chomp ist oft eine sicherere Alternative, da der String unverändert bleibt, wenn er nicht in einem Datensatztrennzeichen endet.

Obwohl dies hauptsächlich zum Entfernen von Trennzeichen verwendet wird, wie \r\nich es verwendet habe, um das letzte Zeichen aus einer einfachen Zeichenfolge zu entfernen, z. B. sum das Wort singulär zu machen.

Kakubei
quelle
1
Würde das nicht einfach das letzte Zeichen entfernen? Die Frage ist über "Zeichen" im Plural
Maetthew
1
Ja, Sie haben Recht, aber chomp ('Zeichen') entfernt die letzten 'Zeichen'. Mir war nicht klar, ob das OP bestimmte Zeichen oder nur N Zeichen wollte.
Kakubei
Ja, das ist die Antwort, nur wenn Sie nur ein Zeichen entfernen möchten (was bei mir allerdings der Fall war).
Quv
29
name = "my text"
x.times do name.chop! end

Hier in der Konsole:

>name = "Nabucodonosor"
 => "Nabucodonosor" 
> 7.times do name.chop! end
 => 7 
> name
 => "Nabuco" 
joscas
quelle
Ist das effizient? Scheint einen Benchmark-Vergleich mit anderen Antworten wert :)
SRack
16

Das Löschen der letzten nZeichen entspricht dem Beibehalten der ersten length - nZeichen.

Active Support enthält String#firstund String#lastMethoden, die eine bequeme Möglichkeit bieten, die ersten / letzten nZeichen beizubehalten oder zu löschen :

require 'active_support/core_ext/string/access'

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

Wenn Sie Schienen verwenden, versuchen Sie:

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

[BEARBEITET]

So erhalten Sie die Zeichenfolge OHNE die letzten 2 Zeichen:

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

Hinweis: Das letzte Zeichenfolgenzeichen ist bei n-1. Um die letzten 2 zu entfernen, verwenden wir n-3.

Guihen
quelle
2
Dadurch werden die letzten beiden Buchstaben nicht entfernt. Es gibt sie zurück.
JP Silvashy
1
Sie müssen den String nicht zuerst messen, Ruby verwendet nativ negative Indizes vom Ende des Strings
Devon Parsons
3

Sie können immer so etwas verwenden

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

Wo X ist die Anzahl der zu entfernenden Zeichen?

Oder mit Zuweisen / Verwenden des Ergebnisses:

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

Wo Xist die Anzahl der Zeichen plus eins zu entfernen.

Zsolt Botykai
quelle
3

Überprüfen Sie die slice()Methode:

http://ruby-doc.org/core-2.5.0/String.html#method-i-slice

Cody Caughlan
quelle
2
Beachten Sie, dass dies nur in Ruby 1.9 funktioniert. In Ruby 1.8 werden dadurch die letzten Bytes entfernt , nicht die letzten Zeichen .
Jörg W Mittag
1

Wenn Sie mit dem Erstellen von Klassenmethoden einverstanden sind und die Zeichen, die Sie abhacken, möchten, versuchen Sie Folgendes:

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
quelle
0

Regex verwenden:

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

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

Wenn die Zeichen, die Sie entfernen möchten, immer dieselben Zeichen sind, ziehen Sie chomp in Betracht:

'abc123'. chomp ('123')

Poonkodi
quelle
-3
x = "my_test"
last_char = x.split('').last
Reshmi Mitra
quelle
3
Frage war über das Entfernen der letzten N Zeichen aus einer Zeichenfolge, nicht über das Finden des letzten Zeichens
unnitallman