Какой самый простой способ удалить первый символ из строки?


174

Пример:

[12,23,987,43

Какой самый быстрый и самый эффективный способ удалить " [", используя, возможно, chop()но для первого символа?


1
Я отредактировал свой ответ, так что может быть возможно изменить выбранный ответ. Посмотрите, сможете ли вы присуждать его ответу Джейсона Стирка, поскольку он самый быстрый и хорошо читаемый.
Жестянщик

3
Используйте str [1 ..- 1], это быстрее всего согласно ответам ниже.
Ачют Растоги

1
Начиная с Ruby 2.5 вы можете использовать delete_prefixи delete_prefix!- подробнее ниже . У меня не было времени на тесты, но скоро сделаю!
SRack

Обновление: я протестировал новые методы ( delete_prefix\ delete_prefix!), и они довольно быстрые. Не совсем удачные предыдущие фавориты по скорости, но читабельность означает, что у них есть отличные новые возможности!
SRack

Ответы:


233

Я предпочитаю использовать что-то вроде:

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

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

Я всегда ищу самый быстрый и самый читаемый способ делать вещи:

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

Запуск на моем 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)

Обновление, чтобы включить еще один предложенный ответ:

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

Что приводит к:

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)

И еще одно использование, /^./чтобы найти первого персонажа:

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

Что приводит к:

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

Вот еще одно обновление более быстрого оборудования и более новой версии 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)

Почему gsub такой медленный?

После выполнения поиска / замены gsubнеобходимо проверить наличие возможных дополнительных совпадений, прежде чем он сможет определить, завершено ли оно. subтолько делает один и заканчивает. Считайте, gsubчто это как минимум два subзвонка.

Кроме того, важно помнить об этом gsub, а subтакже может быть затруднен плохо написанным регулярным выражением, которое сопоставляется гораздо медленнее, чем поиск по подстроке. Если возможно, закрепите регулярное выражение, чтобы получить от него максимальную скорость. Здесь есть ответы на Stack Overflow, демонстрирующие это, поэтому ищите, если вам нужно больше информации.


34
Важно отметить, что это будет работать только в Ruby 1.9. В Ruby 1.8 это удалит первый байт из строки, а не первый символ, чего не хочет OP.
Йорг Миттаг

+1: я всегда забываю, что строковой позиции вы можете назначить не только один символ, но и вставить подстроку. Спасибо!
Кецалькоатль

"[12,23,987,43".delete "["
rupweb

4
Это удаляет его со всех позиций, чего не хочет ОП: «... для первого символа?».
Жестянщик

2
" what about "[12,23,987,43".shift ?"? Как насчет "[12,23,987,43".shift NoMethodError: undefined method shift 'для "[12,23,987,43": String`?
Жестянщик

293

Аналогично ответу Пабло выше, но очиститель тени:

str[1..-1]

Вернет массив от 1 до последнего символа.

'Hello World'[1..-1]
 => "ello World"

13
+1 Посмотрите на результаты тестов, которые я добавил к своему ответу. У вас самое быстрое время выполнения, плюс я думаю, что это очень чисто.
Жестянщик

Как насчет производительности по str[1,]сравнению с вышеупомянутым?
Бор

1
@ Бор: str[1,]вернуть вам 2-й символ, поскольку диапазон 1:nil. Вам нужно будет str[1,999999]указать фактическую вычисленную длину или что-то, что гарантированно будет больше длины, например, (используйте, конечно, int_max), чтобы получить весь хвост. [1..-1]это чище и, вероятно, быстрее, так как вам не нужно работать с длиной вручную (см. [1..length] в тесте производительности)
quetzalcoatl

4
Очень хорошее решение. Кстати, если кто-то хочет удалить первый и последний символы:str[1..-2]
pisaruk

50

Мы можем использовать ломтик, чтобы сделать это:

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

Используя slice!мы можем удалить любой символ, указав его индекс.


2
Этот элегантный slice!(0)ответ действительно должен быть выбран, так как использование asdf[0] = '' для удаления первого символа смешно (так же, как использование gsub с регулярным выражением и стрельба по мухе с помощью гаубицы).
f055

1
Хотя это может показаться на первый взгляд не интуитивным, []=но не требует столько базового кода C, где slice!требуется дополнительная работа. Это добавляет. Аргумент может быть «Что более читабельно?» Я нахожу использование []=читабельным, но я исхожу из фона C -> Perl, который, вероятно, окрашивает мое мышление. Разработчики Java, вероятно, подумают, что это менее читабельно. Любой из них является приемлемым способом решения этой задачи, если он прост в понимании и обслуживании и не требует чрезмерной загрузки ЦП.
Жестянщик

Хорошо. Знаете ли вы, как мы можем измерить, если функция принимает большую нагрузку на процессор в ROR? или мы должны использовать разницу во времени выполнения в миллисекундах или наносекундах?
balanv

18

Ruby 2.5+

Начиная с Ruby 2.5 вы можете использовать delete_prefixили delete_prefix!для достижения этого в удобочитаемой форме.

В этом случае "[12,23,987,43".delete_prefix("[").

Больше информации здесь:

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

Примечание: вы также можете использовать это для удаления элементов из конца строки с помощью delete_suffixиdelete_suffix!

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

Редактировать:

Используя настройку Tin Man, он тоже выглядит довольно быстро (под двумя последними записями delete_pи delete_p!). Не совсем уместен предыдущий фаворит для скорости, хотя очень читабелен.

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)


14

Если вы всегда хотите снять ведущие скобки:

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

Если вы просто хотите удалить первый символ и знаете, что его не будет в многобайтовом наборе символов:

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

или

"[12,23,987,43".slice(1..-1)

1
Я бы использовал "[12,23,987,43".sub(/^\[+/, "")вместо gsub(/^\[/, ""). Первый позволяет механизму регулярных выражений находить все совпадения, затем они заменяются одним действием и приводят к увеличению скорости примерно в 2 раза с Ruby 1.9.3.
Жестянщик

1
Так как мы имеем дело со строками, это должно быть gsub(/\A\[/, "") ?
Сагар


4

Например: a = "Один Два Три"

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" 

Таким образом, вы можете удалить один за другим первый символ строки.


3
Это то же самое, что ответ Джейсона Стирка, только его был представлен много месяцев назад.
Жестянщик

3

Простой способ:

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

removed = str[1..str.length]

Удивительный способ:

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

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

(Примечание: предпочитаю легкий способ :))


1
Если вы хотите сохранить семантику «рубить», вы можете просто"[12,23,987,43".reverse.chop.reverse
Крис Хилд

это довольно большие потери производительности, чтобы убрать один символ
Пабло Фернандес

7
почему бы не использовать [1 ..- 1] вместо [1..self.length]?
скакун

Пример с патчами для обезьян довольно неприхотлив в этом вопросе, это просто неуместное и уродливое ИМО.
Дредозубов

3

Спасибо @ the-tin-man за сбор данных!

Увы, мне не очень нравятся эти решения. Либо они требуют дополнительного шага для получения результата ( [0] = '', .strip!), либо они не очень семантически / не понимают, что происходит ( [1..-1]: «Гм, диапазон от 1 до отрицательного 1? Yearg?»), Или они медленны или длинны до выпиши ( .gsub, .length).

То, что мы пытаемся сделать, это «сдвиг» (на языке Array), но возвращающий оставшиеся символы, а не то, что было смещено. Давайте использовать наш Ruby, чтобы сделать это возможным со строками! Мы можем использовать операцию быстрой скобки, но дать ей хорошее имя и взять аргумент, чтобы указать, сколько мы хотим сжать с фронта:

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

Но есть еще кое-что, что мы можем сделать с помощью этой быстрой, но громоздкой операции с брекетами. Пока мы это делаем, для полноты давайте напишем a #shiftи #firstдля String (почему Array должен быть всем забавным), взяв аргумент arg, чтобы указать, сколько символов мы хотим удалить с самого начала:

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

Хорошо, теперь у нас есть хороший четкий способ вытаскивания символов с начала строки с помощью метода, который соответствует Array#firstи Array#shift(который действительно должен быть методом взрыва ??). И мы можем легко получить измененную строку с помощью #eat!. Хм, мы должны поделиться нашей новой eat!силой с массивом? Почему нет!

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

Теперь мы можем:

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

Так-то лучше!


2
Я помню дискуссию много лет назад на форумах по Perl о такой функции с именем chip()вместо chop()chimp()в качестве аналога chomp()).
Марк Томас

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

str[0] = ""

7
Важно отметить, что это будет работать только в Ruby 1.9. В Ruby 1.8 это удалит первый байт из строки, а не первый символ, чего не хочет OP.
Йорг Миттаг


0

Используя регулярное выражение:

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

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

0

Я нахожу хорошее решение str.delete(str[0])для его читабельности, хотя я не могу засвидетельствовать его производительность.


0

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

# => [2,3,4]

List удаляет один или несколько элементов с начала массива, не изменяет массив и возвращает сам массив вместо удаленного элемента.

Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.