Apa cara termudah untuk menghapus karakter pertama dari sebuah string?

174

Contoh:

[12,23,987,43

Apa cara tercepat dan paling efisien untuk menghapus " [", menggunakan mungkin chop()tetapi untuk karakter pertama?

NullVoxPopuli
sumber
1
Saya mengedit jawaban saya, jadi mungkin saja mengubah jawaban yang Anda pilih. Lihat apakah Anda dapat menghadiahkannya pada jawaban Jason Stirk karena jawabannya adalah yang tercepat, dan sangat mudah dibaca.
the Tin Man
3
Gunakan str [1 ..- 1], yang tercepat menurut jawaban di bawah ini.
Achyut Rastogi
1
Pada Ruby 2.5 Anda dapat menggunakan delete_prefixdan delete_prefix!- detail lebih lanjut di bawah ini . Saya tidak punya waktu untuk melakukan benchmark, tetapi akan segera melakukannya!
SRack
Pembaruan: Saya telah membuat tolok ukur metode baru ( delete_prefix\ delete_prefix!) dan cukup cepat. Tidak cukup mem-favorit kecepatan sebelumnya, tetapi keterbacaan berarti mereka adalah opsi baru yang hebat untuk dimiliki!
SRack

Jawaban:

233

Saya agak suka menggunakan sesuatu seperti:

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

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

Saya selalu mencari cara tercepat dan paling mudah dibaca dalam melakukan sesuatu:

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

Berjalan di Mac Pro saya:

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)

Memperbarui untuk memasukkan satu lagi jawaban yang disarankan:

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

Yang mengakibatkan:

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)

Dan yang lain menggunakan /^./untuk menemukan karakter pertama:

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

Yang mengakibatkan:

# >> 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)

Berikut ini pembaruan lain pada perangkat keras yang lebih cepat dan versi Ruby yang lebih baru:

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)

Mengapa gsub sangat lambat?

Setelah melakukan pencarian / ganti, gsubharus memeriksa kemungkinan kecocokan tambahan sebelum dapat mengetahui apakah sudah selesai. subhanya melakukan satu dan selesai. Anggaplah gsubitu minimal dua subpanggilan.

Juga, penting untuk diingat itu gsub, dan subjuga bisa cacat oleh regex yang ditulis dengan buruk yang cocok jauh lebih lambat daripada pencarian sub-string. Jika mungkin jangkar regex untuk mendapatkan kecepatan maksimal darinya. Ada jawaban di sini di Stack Overflow yang menunjukkan hal itu jadi cari di sekitar jika Anda ingin informasi lebih lanjut.

Manusia Timah
sumber
34
Penting untuk dicatat bahwa ini hanya akan berfungsi di Ruby 1.9. Di Ruby 1.8, ini akan menghapus byte pertama dari string, bukan karakter pertama, yang bukan yang diinginkan OP.
Jörg W Mittag
+1: Saya selalu lupa bahwa untuk posisi string Anda tidak hanya dapat menetapkan satu karakter, tetapi juga Anda dapat memasukkan substring. Terima kasih!
quetzalcoatl
"[12,23,987,43".delete "["
rupweb
4
Itu menghapusnya dari semua posisi, yang bukan yang diinginkan OP: "... untuk karakter pertama?".
the Tin Man
2
" what about "[12,23,987,43".shift ?"? Bagaimana dengan "[12,23,987,43".shift NoMethodError: undefined method shift 'for "[12,23,987,43": String`?
the Tin Man
293

Mirip dengan jawaban Pablo di atas, tetapi pembersih warna:

str[1..-1]

Akan mengembalikan array dari 1 ke karakter terakhir.

'Hello World'[1..-1]
 => "ello World"
Jason Stirk
sumber
13
+1 Lihatlah hasil tolok ukur yang saya tambahkan ke jawaban saya. Anda mendapatkan waktu tercepat, ditambah lagi saya pikir ini sangat bersih.
the Tin Man
Bagaimana dengan kinerja str[1,]dibandingkan dengan di atas?
Bohr
1
@ Bob: str[1,]mengembalikan karakter ke-2 karena rentangnya 1:nil. Anda harus memberikan panjang yang dihitung sebenarnya, atau sesuatu yang dijamin lebih tinggi dari panjang, seperti, str[1,999999](gunakan int_max tentu saja) untuk mendapatkan seluruh ekor. [1..-1]lebih bersih dan mungkin lebih cepat, karena Anda tidak perlu beroperasi dengan panjang secara manual (lihat [1..length] dalam tolok ukur)
quetzalcoatl
4
Solusi yang sangat bagus Omong-omong jika seseorang ingin menghapus karakter pertama dan terakhir:str[1..-2]
pisaruk
50

Kita dapat menggunakan slice untuk melakukan ini:

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

Menggunakan slice!kami dapat menghapus karakter apa pun dengan menentukan indeksnya.

balanv
sumber
2
slice!(0)Jawaban elegan ini benar-benar harus dipilih, karena menggunakan asdf[0] = '' untuk menghapus karakter pertama adalah konyol (seperti menggunakan gsub dengan regex dan menembak lalat dengan howitzer).
f055
1
Meskipun mungkin tampak tidak intuitif di permukaan, []=tidak memerlukan banyak kode C yang mendasarinya, di mana slice!membutuhkan kerja tambahan. Itu bertambah. Argumennya mungkin "Mana yang lebih mudah dibaca?" Saya menemukan menggunakan []=dibaca, tapi saya berasal dari latar belakang C -> Perl yang mungkin mewarnai pemikiran saya. Pengembang Java mungkin akan berpikir itu kurang mudah dibaca. Entah merupakan cara yang dapat diterima untuk menyelesaikan tugas selama itu mudah dipahami dan dipelihara dan tidak memuat CPU secara berlebihan.
the Tin Man
Baik. Apakah Anda tahu bagaimana kami dapat mengukur jika suatu fungsi mengambil banyak beban CPU dalam ROR? atau haruskah kita menggunakan perbedaan waktu eksekusi dalam mili atau nanodetik?
balanv
18

Ruby 2.5+

Pada Ruby 2.5 Anda dapat menggunakan delete_prefixatau delete_prefix!untuk mencapai ini dengan cara yang dapat dibaca.

Dalam hal ini "[12,23,987,43".delete_prefix("[").

Info lebih lanjut di sini:

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

NB Anda juga dapat menggunakan ini untuk menghapus item dari ujung string dengan delete_suffixdandelete_suffix!

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

Edit:

Menggunakan pengaturan tolok ukur Tin Man, tampilannya juga cukup cepat (di bawah dua entri terakhir delete_pdan delete_p!). Tidak cukup mengunggah fave sebelumnya untuk kecepatan, meskipun sangat mudah dibaca.

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
sumber
17

Saya lebih memilih ini:

str = "[12,23,987,43"
puts str[1..-1]
>> 12,23,987,43
henriquesuda
sumber
3
Anda mungkin ingin memeriksa jawaban lain sebelum mengulanginya. Ini sudah disarankan oleh stackoverflow.com/a/3614642/128421
the Tin Man
14

Jika Anda selalu ingin menghapus tanda kurung terkemuka:

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

Jika Anda hanya ingin menghapus karakter pertama, dan Anda tahu itu tidak akan berada dalam set karakter multibyte:

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

atau

"[12,23,987,43".slice(1..-1)
Chris Heald
sumber
1
Saya akan menggunakan "[12,23,987,43".sub(/^\[+/, "")sebagai gantinya gsub(/^\[/, ""). Yang pertama memungkinkan mesin regex menemukan semua kecocokan kemudian diganti dalam satu aksi dan menghasilkan peningkatan kecepatan 2x dengan Ruby 1.9.3.
the Tin Man
1
Karena kita berhadapan dengan string, haruskah ini gsub(/\A\[/, "") ?
Sagar Pandya
6

Alternatif tidak efisien:

str.reverse.chop.reverse
Jaamun
sumber
4

Misalnya: a = "Satu Dua Tiga"

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" 

Dengan cara ini Anda dapat menghapus satu per satu karakter pertama dari string.

Rubyist
sumber
3
Ini sama dengan jawaban Jason Stirk yang hanya diajukannya berbulan-bulan sebelumnya.
the Tin Man
3

Jalan mudah:

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

removed = str[1..str.length]

Cara yang mengagumkan:

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

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

(Catatan: lebih suka cara mudah :))

Pablo Fernandez
sumber
1
Jika Anda ingin melestarikan semantik "chop" yang Anda bisa"[12,23,987,43".reverse.chop.reverse
Chris Heald
itu adalah overhead kinerja yang cukup besar hanya untuk melepaskan satu arang
Pablo Fernandez
7
mengapa tidak menggunakan [1 ..- 1] daripada [1..self.length]?
horseyguy
Contoh Monkey patching cukup bagus untuk pertanyaan ini, itu hanya IMO yang tidak relevan dan jelek.
dredozubov
3

Terima kasih kepada @ the-tin-man karena telah menyusun tolok ukur!

Sayangnya, saya tidak terlalu suka solusi itu. Entah mereka memerlukan langkah ekstra untuk mendapatkan hasil ( [0] = '', .strip!) atau mereka tidak terlalu semantik / jelas tentang apa yang terjadi ( [1..-1]: "Um, rentang dari 1 hingga negatif 1? Yearg?"), Atau mereka lambat atau panjang untuk tulis ( .gsub, .length).

Apa yang kami coba adalah 'shift' (dalam bahasa Array), tetapi mengembalikan karakter yang tersisa, alih-alih apa yang dialihkan. Mari gunakan Ruby kami untuk memungkinkan ini dengan string! Kita dapat menggunakan operasi braket cepat, tetapi berikan nama yang bagus, dan ambil argumen untuk menentukan seberapa banyak kita ingin mengompromikan bagian depan:

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

Tetapi ada lebih banyak yang bisa kita lakukan dengan operasi braket yang cepat tapi tidak berat. Sementara kita berada di dalamnya, untuk kelengkapan, mari kita menulis a #shiftdan #firstuntuk String (mengapa Array harus bersenang-senang‽‽), mengambil argumen untuk menentukan berapa banyak karakter yang ingin kita hapus dari awal:

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, sekarang kita memiliki cara yang jelas untuk menarik karakter dari bagian depan string, dengan metode yang konsisten dengan Array#firstdan Array#shift(yang benar-benar harus menjadi metode bang ??). Dan kita juga dapat dengan mudah mendapatkan string yang dimodifikasi #eat!. Hm, haruskah kita berbagi eat!kekuatan baru kita dengan Array? Kenapa tidak!

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

Sekarang kita bisa:

> 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] 

Itu lebih baik!

brookr
sumber
2
Saya ingat diskusi bertahun-tahun lalu di forum Perl tentang fungsi seperti itu dengan nama chip()bukan chop()(dan chimp()sebagai analog chomp()).
Mark Thomas
2
str = "[12,23,987,43"

str[0] = ""
Pengrajin
sumber
7
Penting untuk dicatat bahwa ini hanya akan berfungsi di Ruby 1.9. Di Ruby 1.8, ini akan menghapus byte pertama dari string, bukan karakter pertama, yang bukan yang diinginkan OP.
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
sumber
0

Menggunakan regex:

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

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

Saya menemukan solusi yang bagus str.delete(str[0])untuk keterbacaannya, meskipun saya tidak bisa membuktikan kinerjanya.

Zeitchef
sumber
0

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

# => [2,3,4]

Daftar menjatuhkan satu atau lebih elemen dari awal array, tidak bermutasi array, dan mengembalikan array itu sendiri, bukan elemen yang dijatuhkan.

Aaron Henderson
sumber