» Главная
eXcode.ru » Статьи » Ruby
» Новости
» Опросы
» Файлы
» Журнал



Пользователей: 0
Гостей: 20







Подводные камни String#sub




Передо мной стояла банальная задача зачитать содержимое файла и произвести подстановку в определенной строке, взяв значение из переменной окружения (Environment variable). Казалось бы, ничего сложного тут нет.

Предположим что необходимая строка содержится в переменной str:

s = "Use ruby. Be happy!"

и для того, что бы заменить слово ruby на Ruby необходимо воспользоваться методом sub:

s = "Use ruby. Be happy!"
puts s.sub( 'ruby', 'Ruby' ) #=> Use Ruby. Be happy!

Ничего сложного тут нет, все отработало как мы и ожидали. А теперь вспомним об одной полезной особенности String#sub -- в качестве первого аргумента может быть использовано регулярное выражение, например:

puts "0001".sub( /0+/, '0' ) #=> 01

Это типичное использование данного метода. Однако иногда необходим произвести замену воспользовавшись значением являющимся частью совпадающей строки, например у нас есть строка "I love @Ruby@" и мы хотим преобразовать её в "I love Ruby" (выкинуть символ @), при этом слово Ruby может быть написано как с заглавной, так и с прописной буквы: Ruby или ruby, и нам важно сохранить тот регистр, который был указан в исходной строке, для этого воспользуемся таким кодом:

puts "I love @Ruby@".sub( /@([Rr]uby)@/, '\1' ) #=> I love Ruby

Возможно, для кого то понадобится объяснение этого кода. Так вот регулярное выражение /@([Rr]uby)@/ читается так: находим подстроку начинающуюся с символа @, дальше может идти либо символ R, либо r (Ruby и ruby это взаимно заменяемые названия нашего любимого языка программирования, хотя Ruby более корректно), затем должна следовать последовательность из символов uby, и в конце опять символ @. Круглые скобки говорят о том что подстрока между символами @ должна быть сохранена для дальнейшего использования.

Зачем нам этот трюк с круглыми скобками и \1, ведь мы могли бы просто написать:

puts "I love @Ruby@".sub( /@[Rr]uby@/, '?uby' )

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

Я уже сказал что круглыми скобками мы воспользовались для сохранения группы символов между @, теперь рассмотрим второй аргумент, передуваемый в метод sub -- '\1'. \1 это спец последовательность символов которая распознается методом sub. Во время своей работы метод sub, в случае удачного совпадения, производит замену совпавшей строки на значение, переданное вторым аргументом, но перед тем как сделать замену, этот аргумент особым образом трансформируется. Если будет встречена последовательность символов \[1-9], то эти последовательности будут заменены на подстроки из совпавшей строки -- в нашем случае подстрока одна ([Rr]uby), но их количество может достигать 9, при этом если подстрока пуста или не задана, то подставляется пустое значение. То есть если наш пример модифицировать следующим образом:

puts "I love @Ruby@".sub( /@([Rr]uby)@/, '\1\2\3' ) #=> I love Ruby

То на результате это ни как не отразится.

А вот теперь самое интересное, ради чего собственно и задумалась эта мини-статья и на какие грабли я встал решая свою исходную задачу -- замену подстроки на значение из переменной окружения.

Заменять необходимо было определенную последовательность символов %PLACE_HOLDER%, значение необходимо было брать из переменной окружения CUSTOMER_ID.

Изначальный вариант решения, который пришел мне в голову, был таков:

str.sub!( '%PLACE_HOLDER%', ENV['CUSTOMER_ID'].to_s )

И это решение работало, до того момента как в CUSTOMER_ID не было сохранено значение 'CUST_ID\12', в этом случае %PLACE_HOLDER% раскрылось в совсем неожиданное для меня значение -- CUST_ID2. Объясняется это тем что метод sub проанализировал значение переменной и в качестве \1 попробовал подставить значение сохраненной подстроки из первого аргумента (%PLACE_HOLDER%), и оно естественно оказалось пустым, т.к. первый аргумент не был даже регулярным выражением!

Этот факт меня очень расстроил, ведь '%PLACE_HOLDER%' даже не регулярное выражение, зачем пытаться обрабатывать последовательность \[1-9]. Эх блин багописцы, подумал я и принялся придумывать как обойти проблему, в результате код был переписан в следующий:

str.sub!( '%PLACE_HOLDER%' ) {
 ENV['CUSTOMER_ID'] ).to_s
}

В данном случае магическая последовательность \[1-9] теряет свое воздействие на String#sub и подставляется как есть!

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

На этом спешу откланится, удачи!

К началу статьи




Добавил: MadvEXДата публикации: 2008-02-11 00:29:28

Рейтинг статьи:0.00 [Голосов 0]Кол-во просмотров: 2155
Оцените статью:

Комментарии читателей

Всего комментариев: 0
Ваше имя: *
Текст записи: *
Имя:

Пароль:



Регистрация

Как вы относитесь к рекламе на сайтах.
Отрицательно, терпеть ее не могу!
46% (95)
С пониманием
25% (51)
Пусть будет, если только по делу
15% (32)
Она мне безразлична!
11% (23)
Я ее обожаю!
3% (6)

Проголосовало: 208
Объявление в газету в разделе трудоустройство. Для ухода за пожилым программистом требуется приятная женщина, говорящая на FORTRAN, BASIC и С++.
Рейтинг: 7/10 (4)
Посмотреть все анекдоты

 
eXcode.ru » Статьи » Ruby