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

174

Пример:

[12,23,987,43

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

NullVoxPopuli
источник
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!мы можем удалить любой символ, указав его индекс.

balanv
источник
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)
SRack
источник
17

Я предпочитаю это:

str = "[12,23,987,43"
puts str[1..-1]
>> 12,23,987,43
henriquesuda
источник
3
Возможно, вы захотите проверить другие ответы, прежде чем повторять их. Это уже было предложено stackoverflow.com/a/3614642/128421
Жестянщик
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\[/, "") ?
Сагар
6

Неэффективная альтернатива:

str.reverse.chop.reverse
jaamun
источник
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" 

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

Rubyist
источник
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] 

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

brookr
источник
2
Я помню дискуссию много лет назад на форумах по Perl о такой функции с именем chip()вместо chop()chimp()в качестве аналога chomp()).
Марк Томас
2
str = "[12,23,987,43"

str[0] = ""
Handcraftsman
источник
7
Важно отметить, что это будет работать только в Ruby 1.9. В Ruby 1.8 это удалит первый байт из строки, а не первый символ, чего не хочет OP.
Йорг Миттаг
0
class String
  def bye_felicia()
    felicia = self.strip[0] #first char, not first space.
    self.sub(felicia, '')
  end
end
Джош Броди
источник
0

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

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

str[/.{#{str.size-n}}\z/] #=> "tring"
Сагар Пандя
источник
0

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

zeitchef
источник
0

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

# => [2,3,4]

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

Аарон Хендерсон
источник