Was ist der einfachste Weg, um das erste Zeichen aus einer Zeichenfolge zu entfernen?

174

Beispiel:

[12,23,987,43

Was ist der schnellste und effizienteste Weg, um das " [" zu entfernen , indem Sie vielleicht ein chop()aber für das erste Zeichen verwenden?

NullVoxPopuli
quelle
1
Ich habe meine Antwort bearbeitet, sodass Sie möglicherweise Ihre ausgewählte Antwort ändern können. Sehen Sie, ob Sie es Jason Stirk's Antwort geben können, da seine die schnellste und gut lesbare ist.
der Blechmann
3
Verwenden Sie str [1 ..- 1], das gemäß den folgenden Antworten am schnellsten ist.
Achyut Rastogi
1
Ab Ruby 2.5 können Sie delete_prefixund delete_prefix!- weitere Details unten verwenden . Ich hatte keine Zeit zum Benchmarking, werde es aber bald tun!
SRack
Update: Ich habe die neuen Methoden ( delete_prefix\ delete_prefix!) verglichen und sie sind ziemlich schnell. Die vorherigen Favoriten sind nicht ganz auf Geschwindigkeit ausgerichtet, aber Lesbarkeit bedeutet, dass sie großartige neue Optionen sind!
SRack

Antworten:

231

Ich bevorzuge etwas wie:

asdf = "[12,23,987,43"
asdf [0] = '' 

p asdf
# >> "12,23,987,43"

Ich bin immer auf der Suche nach der schnellsten und am besten lesbaren Methode:

require 'benchmark'

N = 1_000_000

puts RUBY_VERSION

STR = "[12,23,987,43"

Benchmark.bm(7) do |b|
  b.report('[0]') { N.times { "[12,23,987,43"[0] = '' } }
  b.report('sub') { N.times { "[12,23,987,43".sub(/^\[+/, "") } }

  b.report('gsub') { N.times { "[12,23,987,43".gsub(/^\[/, "") } }
  b.report('[1..-1]') { N.times { "[12,23,987,43"[1..-1] } }
  b.report('slice') { N.times { "[12,23,987,43".slice!(0) } }
  b.report('length') { N.times { "[12,23,987,43"[1..STR.length] } }

end

Laufen auf meinem Mac Pro:

1.9.3
              user     system      total        real
[0]       0.840000   0.000000   0.840000 (  0.847496)
sub       1.960000   0.010000   1.970000 (  1.962767)
gsub      4.350000   0.020000   4.370000 (  4.372801)
[1..-1]   0.710000   0.000000   0.710000 (  0.713366)
slice     1.020000   0.000000   1.020000 (  1.020336)
length    1.160000   0.000000   1.160000 (  1.157882)

Aktualisierung, um eine weitere vorgeschlagene Antwort aufzunehmen:

require 'benchmark'

N = 1_000_000

class String
  def eat!(how_many = 1)
    self.replace self[how_many..-1]
  end

  def first(how_many = 1)
    self[0...how_many]
  end

  def shift(how_many = 1)
    shifted = first(how_many)
    self.replace self[how_many..-1]
    shifted
  end
  alias_method :shift!, :shift
end

class Array
  def eat!(how_many = 1)
    self.replace self[how_many..-1]
  end
end

puts RUBY_VERSION

STR = "[12,23,987,43"

Benchmark.bm(7) do |b|
  b.report('[0]') { N.times { "[12,23,987,43"[0] = '' } }
  b.report('sub') { N.times { "[12,23,987,43".sub(/^\[+/, "") } }

  b.report('gsub') { N.times { "[12,23,987,43".gsub(/^\[/, "") } }
  b.report('[1..-1]') { N.times { "[12,23,987,43"[1..-1] } }
  b.report('slice') { N.times { "[12,23,987,43".slice!(0) } }
  b.report('length') { N.times { "[12,23,987,43"[1..STR.length] } }
  b.report('eat!') { N.times { "[12,23,987,43".eat! } }
  b.report('reverse') { N.times { "[12,23,987,43".reverse.chop.reverse } }
end

Was in ... endet:

2.1.2
              user     system      total        real
[0]       0.300000   0.000000   0.300000 (  0.295054)
sub       0.630000   0.000000   0.630000 (  0.631870)
gsub      2.090000   0.000000   2.090000 (  2.094368)
[1..-1]   0.230000   0.010000   0.240000 (  0.232846)
slice     0.320000   0.000000   0.320000 (  0.320714)
length    0.340000   0.000000   0.340000 (  0.341918)
eat!      0.460000   0.000000   0.460000 (  0.452724)
reverse   0.400000   0.000000   0.400000 (  0.399465)

Und eine andere /^./, um das erste Zeichen zu finden:

require 'benchmark'

N = 1_000_000

class String
  def eat!(how_many = 1)
    self.replace self[how_many..-1]
  end

  def first(how_many = 1)
    self[0...how_many]
  end

  def shift(how_many = 1)
    shifted = first(how_many)
    self.replace self[how_many..-1]
    shifted
  end
  alias_method :shift!, :shift
end

class Array
  def eat!(how_many = 1)
    self.replace self[how_many..-1]
  end
end

puts RUBY_VERSION

STR = "[12,23,987,43"

Benchmark.bm(7) do |b|
  b.report('[0]') { N.times { "[12,23,987,43"[0] = '' } }
  b.report('[/^./]') { N.times { "[12,23,987,43"[/^./] = '' } }
  b.report('[/^\[/]') { N.times { "[12,23,987,43"[/^\[/] = '' } }
  b.report('sub+') { N.times { "[12,23,987,43".sub(/^\[+/, "") } }
  b.report('sub') { N.times { "[12,23,987,43".sub(/^\[/, "") } }
  b.report('gsub') { N.times { "[12,23,987,43".gsub(/^\[/, "") } }
  b.report('[1..-1]') { N.times { "[12,23,987,43"[1..-1] } }
  b.report('slice') { N.times { "[12,23,987,43".slice!(0) } }
  b.report('length') { N.times { "[12,23,987,43"[1..STR.length] } }
  b.report('eat!') { N.times { "[12,23,987,43".eat! } }
  b.report('reverse') { N.times { "[12,23,987,43".reverse.chop.reverse } }
end

Was in ... endet:

# >> 2.1.5
# >>               user     system      total        real
# >> [0]       0.270000   0.000000   0.270000 (  0.270165)
# >> [/^./]    0.430000   0.000000   0.430000 (  0.432417)
# >> [/^\[/]   0.460000   0.000000   0.460000 (  0.458221)
# >> sub+      0.590000   0.000000   0.590000 (  0.590284)
# >> sub       0.590000   0.000000   0.590000 (  0.596366)
# >> gsub      1.880000   0.010000   1.890000 (  1.885892)
# >> [1..-1]   0.230000   0.000000   0.230000 (  0.223045)
# >> slice     0.300000   0.000000   0.300000 (  0.299175)
# >> length    0.320000   0.000000   0.320000 (  0.325841)
# >> eat!      0.410000   0.000000   0.410000 (  0.409306)
# >> reverse   0.390000   0.000000   0.390000 (  0.393044)

Hier ist ein weiteres Update für schnellere Hardware und eine neuere Version von Ruby:

2.3.1
              user     system      total        real
[0]       0.200000   0.000000   0.200000 (  0.204307)
[/^./]    0.390000   0.000000   0.390000 (  0.387527)
[/^\[/]   0.360000   0.000000   0.360000 (  0.360400)
sub+      0.490000   0.000000   0.490000 (  0.492083)
sub       0.480000   0.000000   0.480000 (  0.487862)
gsub      1.990000   0.000000   1.990000 (  1.988716)
[1..-1]   0.180000   0.000000   0.180000 (  0.181673)
slice     0.260000   0.000000   0.260000 (  0.266371)
length    0.270000   0.000000   0.270000 (  0.267651)
eat!      0.400000   0.010000   0.410000 (  0.398093)
reverse   0.340000   0.000000   0.340000 (  0.344077)

Warum ist gsub so langsam?

Nach dem Suchen / Ersetzen gsubmuss nach möglichen zusätzlichen Übereinstimmungen gesucht werden, bevor festgestellt werden kann, ob die Suche abgeschlossen ist. submacht nur eins und endet. Stellen gsubSie sich vor, es sind mindestens zwei subAnrufe.

Es ist auch wichtig, sich daran zu erinnern gsub, und es subkann auch durch schlecht geschriebenen regulären Ausdruck behindert werden, der viel langsamer als eine Suche nach Teilzeichenfolgen übereinstimmt. Wenn möglich, verankern Sie den Regex, um die größtmögliche Geschwindigkeit zu erzielen. Hier auf Stack Overflow finden Sie Antworten, die zeigen, dass Sie nach weiteren Informationen suchen müssen.

der Blechmann
quelle
34
Es ist wichtig zu beachten, dass dies nur in Ruby 1.9 funktioniert. In Ruby 1.8 wird dadurch das erste Byte aus der Zeichenfolge entfernt, nicht das erste Zeichen, was vom OP nicht gewünscht wird.
Jörg W Mittag
+1: Ich vergesse immer , dass Sie einer String-Position nicht nur ein einzelnes Zeichen zuweisen können, sondern auch einen Teilstring einfügen können. Vielen Dank!
Quetzalcoatl
"[12,23,987,43".delete "["
Rupweb
4
Das löscht es aus allen Positionen, was das OP nicht wollte: "... für das erste Zeichen?".
der Blechmann
2
" what about "[12,23,987,43".shift ?"? Was ist mit "[12,23,987,43".shift NoMethodError: undefined method shift 'für "[12,23,987,43": String`?
der Blechmann
292

Ähnlich wie Pablos Antwort oben, aber ein Schattenreiniger:

str[1..-1]

Gibt das Array von 1 bis zum letzten Zeichen zurück.

'Hello World'[1..-1]
 => "ello World"
Jason Stirk
quelle
13
+1 Sehen Sie sich die Benchmark-Ergebnisse an, die ich meiner Antwort hinzugefügt habe. Sie haben die schnellste Laufzeit und ich denke, es ist sehr sauber.
der Blechmann
Was ist mit der Leistung von im str[1,]Vergleich zu den oben genannten?
Bohr
1
@Bohr: Gib str[1,]dir das 2. Zeichen zurück, da der Bereich ist 1:nil. Sie müssten die tatsächlich berechnete Länge str[1,999999]angeben oder etwas, das garantiert höher als die Länge ist, wie (verwenden Sie natürlich int_max), um den gesamten Schwanz zu erhalten. [1..-1]ist sauberer und wahrscheinlich schneller, da Sie die Länge nicht manuell bearbeiten müssen (siehe [1..Länge] im Benchmark)
quetzalcoatl
4
Sehr schöne Lösung. Übrigens, wenn man erste und letzte Zeichen entfernen möchte:str[1..-2]
Pisaruk
50

Wir können Slice verwenden, um dies zu tun:

val = "abc"
 => "abc" 
val.slice!(0)
 => "a" 
val
 => "bc" 

Mit können slice!wir jedes Zeichen löschen, indem wir seinen Index angeben.

balanv
quelle
2
Dieses elegante slice!(0)sollte wirklich die ausgewählte Antwort sein, da asdf[0] = '' es lächerlich ist, das erste Zeichen zu entfernen (genau wie gsub mit Regex zu verwenden und eine Fliege mit einer Haubitze zu schießen).
f055
1
Obwohl es auf den ersten Blick []=nicht intuitiv zu sein scheint, erfordert es nicht so viel zugrunde liegenden C-Code, wo slice!zusätzliche Arbeit erforderlich ist. Das summiert sich. Das Argument könnte lauten: "Was ist besser lesbar?" Ich finde die Verwendung []=lesbar, aber ich komme aus einem C -> Perl-Hintergrund, der wahrscheinlich mein Denken beeinflusst. Java-Entwickler würden wahrscheinlich denken, dass es weniger lesbar ist. Beides ist ein akzeptabler Weg, um die Aufgabe zu erfüllen, solange sie leicht verständlich und wartbar ist und die CPU nicht übermäßig belastet.
der Blechmann
OK. Wissen Sie, wie wir messen können, ob eine Funktion in ROR viel CPU-Last beansprucht? oder sollten wir die Ausführungszeitdifferenz in Milli oder Nanosekunden verwenden?
Balanv
18

Ruby 2.5+

Ab Ruby 2.5 können Sie dies lesbar verwenden delete_prefixoder delete_prefix!erreichen.

In diesem Fall "[12,23,987,43".delete_prefix("[").

Mehr Infos hier:

'invisible'.delete_prefix('in') #=> "visible"
'pink'.delete_prefix('in') #=> "pink"

Hinweis: Sie können dies auch verwenden, um Elemente mit delete_suffixund vom Ende einer Zeichenfolge zu entfernendelete_suffix!

'worked'.delete_suffix('ed') #=> "work"
'medical'.delete_suffix('ed') #=> "medical"

Bearbeiten:

Mit dem Benchmark-Setup des Tin Man sieht es auch ziemlich schnell aus (unter den letzten beiden Einträgen delete_pund delete_p!). Paspelt die vorherigen Favoriten nicht ganz auf Geschwindigkeit, ist aber sehr gut lesbar.

2.5.0
              user     system      total        real
[0]       0.174766   0.000489   0.175255 (  0.180207)
[/^./]    0.318038   0.000510   0.318548 (  0.323679)
[/^\[/]   0.372645   0.001134   0.373779 (  0.379029)
sub+      0.460295   0.001510   0.461805 (  0.467279)
sub       0.498351   0.001534   0.499885 (  0.505729)
gsub      1.669837   0.005141   1.674978 (  1.682853)
[1..-1]   0.199840   0.000976   0.200816 (  0.205889)
slice     0.279661   0.000859   0.280520 (  0.285661)
length    0.268362   0.000310   0.268672 (  0.273829)
eat!      0.341715   0.000524   0.342239 (  0.347097)
reverse   0.335301   0.000588   0.335889 (  0.340965)
delete_p  0.222297   0.000832   0.223129 (  0.228455)
delete_p!  0.225798   0.000747   0.226545 (  0.231745)
SRack
quelle
17

Ich bevorzuge das:

str = "[12,23,987,43"
puts str[1..-1]
>> 12,23,987,43
Henriquesuda
quelle
3
Möglicherweise möchten Sie andere Antworten überprüfen, bevor Sie sie wiederholen. Dies wurde bereits von stackoverflow.com/a/3614642/128421
the Tin Man
14

Wenn Sie immer führende Klammern entfernen möchten:

"[12,23,987,43".gsub(/^\[/, "")

Wenn Sie nur das erste Zeichen entfernen möchten und wissen, dass es nicht in einem Multibyte-Zeichensatz enthalten ist:

"[12,23,987,43"[1..-1]

oder

"[12,23,987,43".slice(1..-1)
Chris Heald
quelle
1
Ich würde "[12,23,987,43".sub(/^\[+/, "")anstelle von verwenden gsub(/^\[/, ""). Mit der ersten Option kann die Regex-Engine alle Übereinstimmungen finden, dann werden sie in einer Aktion ersetzt und die Geschwindigkeit von Ruby 1.9.3 wird um das Zweifache verbessert.
der Blechmann
1
Sollte dies so sein, da es sich um Strings handelt gsub(/\A\[/, "") ?
Sagar Pandya
6

Ineffiziente Alternative:

str.reverse.chop.reverse
Jaamun
quelle
4

Zum Beispiel: a = "Eins Zwei Drei"

1.9.2-p290 > a = "One Two Three"
 => "One Two Three" 

1.9.2-p290 > a = a[1..-1]
 => "ne Two Three" 

1.9.2-p290 > a = a[1..-1]
 => "e Two Three" 

1.9.2-p290 > a = a[1..-1]
 => " Two Three" 

1.9.2-p290 > a = a[1..-1]
 => "Two Three" 

1.9.2-p290 > a = a[1..-1]
 => "wo Three" 

Auf diese Weise können Sie nacheinander das erste Zeichen der Zeichenfolge entfernen.

Rubyist
quelle
3
Dies ist die gleiche Antwort wie Jason Stirk, nur seine Antwort wurde viele Monate zuvor eingereicht.
der Blechmann
3

Einfacher Weg:

str = "[12,23,987,43"

removed = str[1..str.length]

Genialer Weg:

class String
  def reverse_chop()
    self[1..self.length]
  end
end

"[12,23,987,43".reverse_chop()

(Hinweis: lieber einfach :))

Pablo Fernandez
quelle
1
Wenn Sie die "Chop" -Semantik beibehalten möchten, können Sie einfach"[12,23,987,43".reverse.chop.reverse
Chris Heald
Das ist ein ziemlich großer Leistungsaufwand, nur um einen Charakter zu entfernen
Pablo Fernandez
7
Warum nicht [1 ..- 1] anstelle von [1..self.length] verwenden?
Horseyguy
Das Beispiel für das Patchen von Affen ist für diese Frage ziemlich falsch, es ist nur irrelevant und hässlich, IMO.
Dredozubov
3

Vielen Dank an @ the-tin-man für die Zusammenstellung der Benchmarks!

Leider mag ich keine dieser Lösungen wirklich. Entweder benötigen sie einen zusätzlichen Schritt, um das Ergebnis zu erhalten ( [0] = '', .strip!), oder sie sind nicht sehr semantisch / klar darüber, was passiert ( [1..-1]: "Ähm, ein Bereich von 1 bis negativ 1? Yearg?"), Oder sie sind langsam oder langwierig schreibe aus ( .gsub, .length).

Was wir versuchen, ist eine 'Verschiebung' (im Array-Sprachgebrauch), die jedoch die verbleibenden Zeichen zurückgibt, anstatt das, was verschoben wurde. Verwenden wir unseren Ruby, um dies mit Strings zu ermöglichen! Wir können die schnelle Klammeroperation verwenden, aber geben Sie ihr einen guten Namen und nehmen Sie ein Argument, um anzugeben, wie viel wir von vorne abkauen möchten:

class String
  def eat!(how_many = 1)
    self.replace self[how_many..-1]
  end
end

Mit dieser schnellen, aber unhandlichen Klammeroperation können wir jedoch noch mehr tun. Wenn wir gerade dabei sind, schreiben wir der Vollständigkeit halber ein #shiftund #firstfür String (warum sollte Array den ganzen Spaß haben) und nehmen ein Argument, um anzugeben, wie viele Zeichen wir von Anfang an entfernen möchten:

class String
  def first(how_many = 1)
    self[0...how_many]
  end

  def shift(how_many = 1)
    shifted = first(how_many)
    self.replace self[how_many..-1]
    shifted
  end
  alias_method :shift!, :shift
end

Ok, jetzt haben wir eine gute Möglichkeit, Zeichen von der Vorderseite eines Strings zu ziehen, mit einer Methode, die mit Array#firstund übereinstimmt Array#shift(was eigentlich eine Knallmethode sein sollte?). Und wir können den modifizierten String auch leicht mit bekommen #eat!. Hm, sollten wir unsere neue eat!Kraft mit Array teilen ? Warum nicht!

class Array
  def eat!(how_many = 1)
    self.replace self[how_many..-1]
  end
end

Jetzt können wir:

> str = "[12,23,987,43" #=> "[12,23,987,43"
> str.eat!              #=> "12,23,987,43"
> str                   #=> "12,23,987,43"

> str.eat!(3)           #=> "23,987,43"
> str                   #=> "23,987,43"

> str.first(2)          #=> "23"
> str                   #=> "23,987,43"

> str.shift!(3)         #=> "23,"
> str                   #=> "987,43"

> arr = [1,2,3,4,5]     #=> [1, 2, 3, 4, 5] 
> arr.eat!              #=> [2, 3, 4, 5] 
> arr                   #=> [2, 3, 4, 5] 

Das ist besser!

brookr
quelle
2
Ich erinnere mich an eine Diskussion vor Jahren in Perl-Foren über eine solche Funktion mit dem Namen chip()anstelle von chop()(und chimp()als Analogon von chomp()).
Mark Thomas
2
str = "[12,23,987,43"

str[0] = ""
Handwerker
quelle
7
Es ist wichtig zu beachten, dass dies nur in Ruby 1.9 funktioniert. In Ruby 1.8 wird dadurch das erste Byte aus der Zeichenfolge entfernt, nicht das erste Zeichen, was vom OP nicht gewünscht wird.
Jörg W Mittag
0
class String
  def bye_felicia()
    felicia = self.strip[0] #first char, not first space.
    self.sub(felicia, '')
  end
end
Josh Brody
quelle
0

Regex verwenden:

str = 'string'
n = 1  #to remove first n characters

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

Ich finde eine gute Lösung str.delete(str[0])für die Lesbarkeit, obwohl ich die Leistung nicht bestätigen kann.

zeitchef
quelle
0

list = [1,2,3,4] list.drop (1)

# => [2,3,4]

List löscht ein oder mehrere Elemente vom Anfang des Arrays, mutiert das Array nicht und gibt das Array selbst anstelle des abgelegten Elements zurück.

Aaron Henderson
quelle