Анимация
JavaScript


Главная  Библионтека 

0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 [ 58 ] 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242

6.5. Поиск N-го совпадения 191

Комментарий

Как объяснялось во введении к этой главе, при наличии модификатора /д в скалярном контексте происходит многократный поиск. Его удобно использовать в циклах while - например, для подсчета совпадений в строке:

# Простой вариант с циклом while $count = О,

while($string =" /РАТ/g) {

$count++, # Или что-нибудь другое

# То же с завершающим циклом while $count = О

$count++ while Sstring =" /РАТ/g

# С циклом for

for (Scount = 0, Sstring =" /РАТ/g, Scount++) { }

# Аналогично, но с подсчетом перекрывающихся совпадений $count++ while Sstring =~ /(?=PAT)/g

Чтобы найти N-й экземпляр, проще всего завести отдельный счетчик Когда он достигнет N, сделайте то, что считаете нужным. Аналогичная методика может применяться и для поиска каждого N-ro совпадения - в этом случае проверяется кратность счетчика N посредством вычисления остатка при делении. Например, проверка (++$count % 3) == О находит каждое третье совпадение.

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

Spond = One fish two fish red fish blue fish ,

# С применением временного массива

©colors = (Spend =" /(w+)\s+fish\b\gi), » Найти все совпадения Scolor = $colors[2], # Выбрать одно,

# интересующее нас

# Без временного массива

Scolor = ( Spond = /(\w+)\s+fish\b/gi )[2], # Выбрать третий элемент

print The third fish is the pond is Scolor \n The third fish in the pond is red.

В другом примере находятся все нечетные совпадения:

Scount = О,

$ = One fish two fish red fish blue fish , ©evens = grep {$count++ % 2 == 1} /(\w+)\s+fish\b/gi, print Even numbered fish are ©evens \n , Even numbered fish are two blue.

При подстановке заменяющая строка должна представлять собой программное выражение, которое возвращает соответствующую строку. Не забывайте возвращать оригинал как заменяющую строку в том случае, если замена не нужна. В следующем примере мы ищем четвертый экземпляр fish и заменяем предшествующее слово другим:



# До конца строки

в результате поиск последнего экземпляра fish принимает следующий вид:

Spond = One fish two fish red fish blue fish If (Spond =" m{

\b ( \w+) \s+ fish \b С?! . \b fish \b ) }six )

print Last fish IS $1/\n } else {

$count = о s{

( \w+) (

\s+ fish \b

if (++$count == 4) {

sushi $2, } else {

$1 $2

}gex

One fish two fish red fish sushi fish

Задача поиска последнего совпадения также встречается довольно часто Простейшее решение - пропустить все начало строки Например, после / *\b(\w+)\s+ f ish\b/ переменная $1 будет содержать слово, предшествующее последнему экземпляру fish .

Другой способ - глобальный поиск в списковом контексте для получения всех совпадений и последующее извлечение нужного элемента этого списка

$pond = One fish two fish red fish blue fish swim here Scolor = ( Spond /\b(\w+)\s+fish\b/gi print Last fish IS Scolor \n , Last fish is blue.

Если потребуется найти последнее совпадение без применения /д, то же самое можно сделать с отрицательной опережающей проверкой С НЕЧТО) Если вас интересует последний экземпляр произвольного шаблона А, вы ищете А, сопровождаемый любым количеством «не-А», до конца строки Обобщенная конструкция имеет вид АС i *А) «$, однако для удобства чтения ее можно разделить

А # Найти некоторый шаблон А

С # При этом не должно находиться

♦ # что-то другое

А # и А



print Failed!\n ,

Last fish is blue.

Такой подход имеет свои преимущества - он ограничивается одним шаблоном и потому подходит для ситуаций, аналогичных описанной в рецепте 6.17. Впрочем, имеются и недостатки. Он однозначно труднее записывается и воспринимается - впрочем, если общий принцип понятен, все выглядит не так плохо. К тому же это решение медленнее работает - для протестированного набора данных быстродействие снижается примерно в два раза.

> Смотри также-

Поведение конструкции т д в скалярном контексте описано в разделе «Regexp Quote-like Operators» per/op(l). Отрицательные опережающие проверки нулевой ширины продемонстрированы в разделе «Regular Expressions perlre(i).

6.6. Межстрочный поиск

Проблема

Требуется использовать регулярные выражения для последовательности, состоящей из нескольких строк Специальные символы (любой символ, кроме перевода строки), " (начало строки) и $ (конец строки), кажется, не работают Это может произойти при одновременном чтении нескольких записей или всего содержимого файла.

Решение

Воспользуйтесь модификатором /т, /з или обоими сразу. Модификатор /з разрешает совпадение с переводом строки (обычно этого не происходит). Если последовательность состоит из нескольких строк, шаблон /foo «Ьаг/в совпадет с foo и Ьа г , находящимися в двух соседних строках. Это не относится к точкам в символьных классах (например, [#% ]), которые всегда представляют собой обычные точки.

Модификатор /т разрешает совпадение " и $ в переводах строк. Например, сЪ-впадение для шаблона /~=head[1-7]$/m возможно не только в начале записи, но и в любом из внутренних переводов строк.

Комментарий

При синтаксическом анализе документов, в которых переводы строк не имеют значения, часто используется «силовое» решение - файл читается по абзацам (а иногда даже целиком), после чего происходит последовательное извлечение лексем. Для успешного межстрочного поиска необходимо, чтобы символ совпадал с переводом строки - обычно этого не происходит. Если в буфер читается сразу несколько строк, вероятно, вы предпочтете, чтобы символы " и $ совпадали с началом и концом внутренних строк, а не всего буфера.

Необходимо хорошо понимать, чем /т отличается от /в: первый заставляет " и $ совпадать на внутренних переводах строк, а второй заставляет совпадать с пере-



0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 [ 58 ] 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242