Извлечение строк из определенного интервала

Проблема

Требуется извлечь все строки, расположенные в определенном интервале. Интервал может быть задан двумя шаблонами (начальным и конечным) или номером первой и последней строки.
Часто встречающиеся примеры — чтение первых 10 строк файла (строки с 1 по 10) или основного текста почтового сообщения (все, что следует после пустой строки).

Решение

Используйте оператор .. или ... для шаблонов или номеров строк. В отличие от .. оператор ... не возвращает истинное значение, если оба условия выполняются в одной строке.
while (<>) {
  if (/НАЧАЛЬНЫЙ ШАБЛОН/../КОНЕЧНЫЙ ШАБЛОН/) {
  # Строка находится между начальным
  # и конечным шаблонами включительно.
  }
}

while (<>) {
  if ($НОМЕР_НАЧАЛЬНОЙ_СТРОКИ .. $НОМЕР_КОНЕЧНОЙ_СТРОКИ) {
  # Строка находится между начальной
  # и конечной включительно.
  }
}
Если первое условие оказывается истинным, оператор ... не проверяет второе условие.
while (<>) {
  if (/НАЧАЛЬНЫЙ ШАБЛОН/.../КОНЕЧНЫЙ ШАБЛОН/) {
  # Строка находится между начальным
  # и конечным шаблонами, расположенными в разных строках.
  }
}

while (<>) {
  if ($НОМЕР_НАЧАЛЬНОЙ_СТРОКИ ... $НОМЕР_КОНЕЧНОЙ_СТРОКИ) {
  # Строка находится между начальной
  # и конечной, расположенными в разных строках.
  }
}

Комментарий

Из бесчисленных операторов Perl интервальные операторы .. и ..., вероятно, вызывают больше всего недоразумений. Они создавались для упрощения выборки интервалов строк, чтобы программисту не приходилось сохранять информацию о состоянии. В скалярном контексте (например, в условиях операторов if и while) эти операторы возвращают true или false, отчасти зависящее от предыдущего состояния. Выражение левый_операнд .. правый_операнд возвращает false, до тех пор, пока левый_операнд не станет истинным. Когда это условие выполняется, левый_операнд перестает вычисляться, а оператор возвращает true до тех пор, пока не станет истинным правый операнд. После этого цикл начинается заново. Другими словами, истинность первого операнда «включает» конструкцию, а истинность второго операнда «выключает» ее.
Условия могут быть абсолютно произвольными. В сущности, границы интервала могут быть заданы проверочными функциями mytestfunc(l) .. mytestfunc(2), хотя на практике это происходит редко. Как правило, операндами интервальных операторов являются либо номера строк (первый пример), шаблоны (второй пример) или их комбинация.
# Вывод всех фрагментов <ХМР> .. </ХМР> из документа HTML
while (<>) {
  print if m#<ХМР>#i .. m#</ХМР>#i;
}
Если хотя бы один из операндов задан в виде числовой константы, интервальные операторы осуществляют неявное сравнение с переменной $. ($NR или $INPUT_LINE_NUMBER при действующей директиве use English). Поосторожнее с неявными числовыми сравнениями! В программе необходимо указывать числовые константы, а не переменные. Это означает, что в условии можно написать 3 .. 5, но не $n .. $m, даже если значения $n и $m равны 3 и 5 соответственно. Вам придется непосредственно проверить переменную $..
$top = 3;
$bottom = 5;
print if $. == $top .. $. == $bottom
Операторы .. и ... отличаются своим поведением в том случае, если оба операнда могут оказаться истинными в одной строке. Рассмотрим два случая:
print if /begin/  .. /end/;
print if /begin/  ... /end/;
Для строки "You may not end here you begin" оба интервальных оператора возвращают true. Однако оператор .. не будет выводить дальнейшие строки. Дело в том, что после выполнения первого условия он проверяет второе условие в той же строке; вторая проверка сообщает о найденном конце интервала. С другой стороны, оператор ... продолжит поиск до следующей строки, в которой найдется /end/, — он никогда не проверяет оба операнда одновременно.
Разнотипные условия можно смешивать:
while (<>)  {
  $in_header = 1 .. /^$/;
  $in_body   = /^$/ .. eof();
}
Переменная $in_header будет истинной, начиная с первой входной строки и заканчивая пустой строкой, отделяющей заголовок от основного текста, — папример, в почтовых сообщениях, новостях Usenet и даже в заголовках HTTP (теоретически строки в заголовках HTTP должны завершаться комбинацией CR/LF, но на практике серверы относятся к их формату весьма либерально). Переменная $in_body становится истинной в момент обнаружения первой пустой строки и до конца файла. Поскольку интервальные операторы не перепроверяют начальное условие, остальные пустые строки (например, между абзацами) игнорируются.
Рассмотрим пример. Следующий фрагмент читает файлы с почтовыми сообщениями и выводит адреса, найденные в заголовках. Каждый адрес выводится один раз. Заголовок начинается строкой "From:" и завершается первой пустой строкой. Хотя это определение и не соответствует RFC-822, оно легко формулируется.
%seen = ();
while (<>)  {
  next unless /^From:?\s/i .. /^$/;
  while (/([^<>(), ;\s]+\@[^<>(),;\s]+)/g) {
    print "$1\n" unless $seen{$1}++;
  }
}

См. также




2013-09-10 17:05:19

Proverte kod v komentariyah gde pro list tam oshibki detskie




Оставить комментарий:
Ваше Имя:
Email:
Антибот: *  
Ваш комментарий: