Рубінова багаторазова заміна рядка


75
str = "Hello☺ World☹"

Очікуваний результат:

"Hello:) World:("

Я можу зробити це: str.gsub("☺", ":)").gsub("☹", ":(")

Чи є інший спосіб, щоб я міг зробити це за один виклик функції ?. Щось на зразок:

str.gsub(['s1', 's2'], ['r1', 'r2'])

1
Чи є причина, чому ви хочете зробити це за один дзвінок? Я хотів би дотримуватися вашого першого рішення.
Simon Perepelitsa

2
@Semyon: Пара таблиць відображення має бути великою, або її можна налаштувати під час виконання.
мю занадто коротке

1
Аналогічно, якщо у вас вийде величезна таблиця відображення - ви в основному дивитесь на мову шаблонів. У такому випадку ви можете перетворити його в DSL і написати інтерпретатор (або компілятор) для цього.
Swanand

Я очікував String#trзробити трюк, але заміни, які є кількома символами, означають, що я не можу цим користуватися.
Ендрю Грімм,

Відповіді:


123

Оскільки Ruby 1.9.2, String#gsubприймає хеш як другий параметр для заміни відповідними ключами. Ви можете використовувати регулярний вираз, щоб відповідати підрядку, який потрібно замінити, і передати хеш для значень, які потрібно замінити.

Подобається це:

'hello'.gsub(/[eo]/, 'e' => 3, 'o' => '*')    #=> "h3ll*"
'(0) 123-123.123'.gsub(/[()-,. ]/, '')    #=> "0123123123"

У Ruby 1.8.7 ви б досягли того ж за допомогою блоку:

dict = { 'e' => 3, 'o' => '*' }
'hello'.gsub /[eo]/ do |match|
   dict[match.to_s]
 end #=> "h3ll*"

вау! Я поняття не мав! чудові речі!
kikito

Круто, не знав про це. Вигляд приємнішої версії Perl tr.
Marnen Laibow-Koser

Зверніть увагу, що це не те саме, що викликати str.gsub (ключ, значення) для кожного елемента хешу. Якщо щось відповідає регулярному виразу, але в хеші немає запису, це буде видалено.
Sprachprofi

3
@NarenSisodiya, насправді це має бути: '(0) 123-123.123'.gsub (/ [() \ - ,.] /,' ') Вам потрібно додати символ втечі до' - '.
jpbalarini

Так, цей рядок помилковий: '(0) 123-123.123'.gsub (/ [() - ,.] /,' ') Вам або потрібно уникнути тире, або перемістити його на передню панель.
Грег Бласс,

40

Створіть таблицю відображення:

map = {'☺' => ':)', '☹' => ':(' }

Потім побудуйте регулярний вираз:

re = Regexp.new(map.keys.map { |x| Regexp.escape(x) }.join('|'))

І нарешті gsub:

s = str.gsub(re, map)

Якщо ви застрягли в 1,8 землі, то:

s = str.gsub(re) { |m| map[m] }

Вам потрібно Regexp.escapeтам, якщо щось, що ви хочете замінити, має особливе значення в регулярному виразі. Або, завдяки steenslag, ви можете використовувати:

re = Regexp.union(map.keys)

і цитування подбає про вас.


@steenslag: Це гарна модифікація.
мю занадто коротке

Рядок # gsub приймає рядки як параметр шаблону: "Шаблон, як правило, є регулярним виразом; якщо він заданий як рядок, будь-які метасимволи регулярних виразів, що містяться в ньому, будуть інтерпретовані буквально, наприклад" \\ d "буде відповідати зворотній реакції, після якої піде" d " , замість цифри. ".
Ендрю Грімм,

@Andrew: Так, але у нас є кілька рядків для заміни, отже, регулярний вираз.
мю занадто коротке

що якщо ключі карти - це вирази регулярних виразів? заміна, здається, не працює
content01

@ content01: Зверху в моїй голові, я думаю, вам доведеться йти по одному в такому випадку:map.each { |re, v| ... }
mu занадто короткий

37

Ви можете зробити щось подібне:

replacements = [ ["☺", ":)"], ["☹", ":("] ]
replacements.each {|replacement| str.gsub!(replacement[0], replacement[1])}

Може бути більш ефективне рішення, але це, принаймні, робить код трохи чистішим


2
Хіба це не має бути replacements.each?
DanneManne

4
Це просто складніше і повільніше.
texasbruce

1
Повернене значення each- це колекція, до якої було викликано. stackoverflow.com/questions/11596879/…
Натан Манусос,

1
щоб він повернув результат і не змінив str:replacements.reduce(str){|str,replacement| str.gsub(replacement[0],replacement[1])}
artm

4
@artm ви також можете робити replacements.inject(str) { |str, (k,v)| str.gsub(k,v) }і уникати необхідності робити [0]і [1].
Ben Lings

19

Пізно на вечірку, але якщо ви хочете замінити певні символи на один, ви можете використовувати регулярний вираз

string_to_replace.gsub(/_|,| /, '-')

У цьому прикладі gsub замінює підкреслення (_), коми (,) або () тире (-)


4
це було б ще краще так:string_to_replace.gsub(/[_- ]/, '-')
Автоматично

5

Ще один простий спосіб, і все ж легкий для читання, полягає в наступному:

str = '12 ene 2013'
map = {'ene' => 'jan', 'abr'=>'apr', 'dic'=>'dec'}
map.each {|k,v| str.sub!(k,v)}
puts str # '12 jan 2013'

5

Ви також можете використовувати tr для заміни кількох символів у рядку одночасно,

Наприклад, замініть "h" на "m" і "l" на "t"

"hello".tr("hl", "mt")
 => "metto"

виглядає просто, акуратно і швидше (хоч і не така різниця), ніж gsub

puts Benchmark.measure {"hello".tr("hl", "mt") }
  0.000000   0.000000   0.000000 (  0.000007)

puts Benchmark.measure{"hello".gsub(/[hl]/, 'h' => 'm', 'l' => 't') }
  0.000000   0.000000   0.000000 (  0.000021)

1

Рифуючи на відповідь Нарена вище , я би пішов

tr = {'a' => '1', 'b' => '2', 'z' => '26'}
mystring.gsub(/[#{tr.keys}]/, tr)

Тож 'zebraazzeebra'.gsub(/[#{tr.keys}]/, tr)повертає "26e2r112626ee2r1"

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.