Анимация
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.2 удаляет теги HTML из всех файлов, переданных в @>ARGV, и отправляет результат в STDOUT. Сначала мы отменяем разделение записей, чтобы при каждой операции чтения читалось содержимое всего файла. Если @>ARGV содержит несколько аргументов, файлов также будет несколько. В этом случае при каждом чтении передается содержимое всего файла. Затем мы удаляем все открывающие и закрывающие угловые скобки и все, что находится между ними. Мы не можем просто воспользоваться * по двум причинам: во-первых, этот шаблон не учитывает закрывающих угловых скобок, а во-вторых, он не поддерживает межстрочных совпадений Проблема решается применением * в сочетании с модификатором /s - по крайней мере, в данном случае.

Пример 6.2. killtags

#i/usr/bin/perl

# killtags - очень плохое удаление тегов HTML

undef $/ # При каждом чтении передается весь файл

while (о) { # Читать по одному файлу

s/< *> gs # Удаление тегов (очень скверное)

print # Вывод файла в STDOUT

Шаблон s/<[">]*> g работает намного быстрее, но такой подход наивен: он приведет к неправильной обработке тегов в комментариях HTML или угловых скобок в кавычках (<IMG SRC= here gif ALT= <<0oh la lai» >). В рецепте 20 6 показано, как решаются подобные проблемы.

Программа из примера 6.3 получает простой текстовый документ и ищет в начале абзацев строки вида Chapter 20 Better Living Through Chemisery . Такие строки оформляются заголовками HTML первого уровня. Поскольку шаблон получился довольно сложным, мы воспользовались модификатором /х, который разрешает внутренние пропуски и комментарии.

Пример 6.3. headerfy

#1/usr/bin/perl

# headerfy оформление заголовков глав в HTML $/ =

while ( о ) { # Получить абзац

\А # Начало записи

( # Сохранить в $1

Chapter # Текстовая строка

\s+ # Обязательный пропуск

\d+ # Десятичное число

\s« # Необязательный пропуск

# Двоеточие

* «Все, кроме перевода строки, до конца строки

}{<Н1>$1</Н1>}дх, print



6.7. Чтение записей с разделением по шаблону 195

Если комментарии лишь затрудняют понимание, ниже тот же пример переписан в виде короткой командной строки:

% perl -ООре s{\A(Chapter\s+\d+\s* •)}{<H1>$1</H1>}gx datafile

Возникает интересная проблема: в одном шаблоне требуется указывать как начало записи, так и конец строки. Начало записи можно было бы определить с помощью ~, но символ $ должен определять не только конец записи, но и конец строки. Мы добавляем модификатор /т, отчего иэденяется смысл как ~, так и $. Начало записи вместо ~ определяется с помощью \А. Кстати говоря, метасимвол \Z (хотя в нашем примере он не используется) совпадает с концом записи даже при наличии модификатора /т.

Следующий пример демонстрирует совместное применение /з и /т. На этот раз мы хотим, чтобы символ " совпадал с началом любой строки абзаца, а точка - с переводом строки. Эти модификаторы никак не связаны, и их совместное применение ничем не ограничено. Стандартная переменная $ содержит число записей последнего прочитанного файла. Стандартная переменная $ARGV содержит файл, автоматически открываемый при обработке <ARGV>.

$/ = » Режим чтения абзацев

while (<ARGV>) {

while (n#"START( *)"END#sn) { # /s - совпадение с переводом строки

# /т - совпадение ~ с началом внутренних строк print chunk $ in $ARGV has «$1»\n ,

Если вы уже привыкли работать с модификатором /ш, то " и $ можно заменить на \А и \Z. Но что делать, если вы предпочитаете /з и хотите сохранить исходный смысл Воспользуйтесь конструкцией ["\п] Если вы не намерены использовать /S, но хотите иметь конструкцию, совпадающую с любым байтом, сконструируйте символьный класс вида [\000-\377] или даже [\d\D]. Использовать [ \п] нельзя, поскольку в символьных классах не обладает особой интерпретацией.

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

Описание переменной $/ ъperlvar{V)\ описание модификаторов /з и /т вperlre(i). Мы вернемся к специальной переменной $/ в главе 8.

6.7. Чтение записей с разделением по шаблону

Проблема

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



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

Решение

Прочитайте весь файл и воспользуйтесь функцией split: undef $/,

©chunks = spllt(/шaблoн/,<ФAЙЛOBЫЙ MAHИПУЛЯTOP>),

Комментарий

Разделитель записей Perl должен быть фиксированной строкой, а не шаблоном (ведь должен awk быть хоть в чем-то лучше!). Чтобы обойти это ограничение, отмените разделитель входных записей, чтобы следующая операция чтения прочитала весь файл. Иногда это называется режимом поглощающего ввода (slurp mode), потому что весь файл поглощается как одна большая строка. Затем разделите эту большую строку функцией split, используя шаблон разделения записей в качестве первого аргумента.

Рассмотрим пример. Допустим, входной поток представляет собой текстовый файл, содержащий строки " Se", " Ch" и " Ss" - служебные коды для макросов troff. Эти строки представляют собой разделители. Мы хотим найти текст, расположенный между ними.

# Ch, Se и Ss отделяют фрагменты данных STDIN {

local $/ = undef,

©chunks = split(/-\,(ChSeSs)$/m, о),

>

print "I read ", scalar(@chunks), chunks \n",

Мы создаем локальную версию переменной $/, чтобы после завершения блока было восстановлено ее прежнее значение. Если шаблон содержит круглые скобки, функция split также возвращает разделители. Это означает, что данные в возвращаемом списке будут чередоваться с элементами "Se", "Ch" и "Ss".

Если разделители вам не нужны, но вы все равно хотите использовать круглые скобки, воспользуйтесь «несохраняющими» скобками в шаблоне вида /A.CiChlSeISs)!/!!!.

Чтобы записи разделялись перед шаблоном, но шаблон включался в возвращаемые записи, воспользуйтесь опережающей проверкой: /? (?=\. С: Ch Se Ss)) /т. В этом случае каждый фрагмент будет начинаться со строки-разделителя.

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



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