Perl захватывает текст между двумя ключевыми словами - PullRequest
0 голосов
/ 17 декабря 2018

Я пытаюсь прочитать в тексте между двумя ключевыми словами.Хотя на самом деле не работает.Все, что я хочу, это прочитать вопрос и ответ, а затем распечатать его.Это не работает, просто продолжает печатать действительно большой цикл.

#!/usr/bin/perl
use strict ;
use warnings;
my $question ;
my $answer ;

while(my $line = <>){
chomp $line ;

if ($line =~ /questionstart(.*)questionend/) {
    $question = $1 ; }
elsif ($line  =~ /answerstart(.*)answerend/) {
    $answer = $1 ; }

my $flashblock = <<"FLASH" ;
<!-- BEGIN -->
<p class="question">
  $question
</p>
<p class="answer">
   $answer
</p>
<!-- END -->
FLASH
print $flashblock ;
}

Это образец файла

questionstart

hellphellohellohello


questionend

answerstart

hellohellohello

answerend

Ответы [ 3 ]

0 голосов
/ 17 декабря 2018

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

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

use warnings;
use strict;
use feature 'say';

my ($question, $answer);
my ($in_Q, $in_A);

while (my $line = <>) {
    next if $line =~ /^\s*$/;

    if    ($line =~ /^\s*questionstart/) { $in_Q = 1; next }   
    elsif ($line =~ /^\s*questionend/)   { $in_Q = 0; next }   
    elsif ($line =~ /^\s*answerstart/)   { $in_A = 1; next }   
    elsif ($line =~ /^\s*answerend/)     { $in_A = 0; next }       

    if    ($in_Q) { $question .= $line }
    elsif ($in_A) { $answer   .= $line }
}

say "Question: $question";
say "Answer: $answer";

(я сократил if-elsif утверждения только для краткости и акцента здесь)

Этот код делает некоторыеРазумные предположения о входном файле.Я требую, чтобы маркеры начинали строку (с возможными пробелами), но учитывали больше текста после них.Если вы хотите убедиться, что они единственные в строке, добавьте якорь $ в конце регулярного выражения (снова с \s*).

Указано, что вход имеет один Q / A,Если он когда-нибудь изменится на несколько, переместите отпечатки в цикле, как только в конце ответа появится значение elsif (/^\s*answerend/) { .. }

Печать в вопросе подойдет, поэтому я не буду повторять это здесь.Если есть возможность напечатать формат, отличный от HTML, очистите результирующие строки от начальных и конечных пробелов, нескольких пробелов и строк новой строки.


Повторные проверки для одной и той же переменной могут привести к одномудля поиска конструкции типа case, что в Perl будет switch .Однако это все еще экспериментальная функция, которая работает так, что

трудно точно описать

(документация!).Кроме того, он также может включать в себя интеллектуальное сопоставление , которое довольно сложно описать, широко понимаемое как сломанное в его нынешней форме и определенно подлежащее изменению.Поэтому я рекомендую придерживаться каскадных операторов if-elsif (в этом подходе).

0 голосов
/ 17 декабря 2018

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

Это идеальное использование для Perl "триггер / флоп"operator (..).

#!/usr/bin/perl

use strict;
use warnings;

my ($question, $answer);

while (<DATA>) {
  if (/questionstart/ .. /questionend/ and ! /question(start|end)/) {
    $question .= $_;
  }

  if (/answerstart/ .. /answerend/ and ! /answer(start|end)/) {
    $answer .= $_;
  }

  # If we're at the end of an answer, do all the stuff
  if (/answerend/) {
    q_and_a($question, $answer);

    # reset text variables
    $question = $answer = '';
  }
}

sub q_and_a {
  my ($q, $a) = @_;

  print <<"FLASH";
<!-- BEGIN -->
<p class="question">
  $question
</p>
<p class="answer">
   $answer
</p>
<!-- END -->
FLASH
}

__DATA__
questionstart

hellphellohellohello


questionend

answerstart

hellohellohello

answerend

Обновление: Переместил отображение в подпрограмму, чтобы очистить основной цикл.

0 голосов
/ 17 декабря 2018

Ваш подход предполагает чтение файла построчно, но ваше регулярное выражение пытается перехватить несколько строк между началом и концом вопроса / ответа.Ни одна строка в вашем файле не будет соответствовать такому многострочному регулярному выражению, и в конечном итоге вы получите неинициализированные переменные $question и $answer и блок / предупреждение, напечатанные для каждой строки в вашем файле.

Имеет смысл прочитать весь текстовый файл в строку, а затем разбить его на блоки вопросов / ответов и обрезать содержимое (при желании):

#!/usr/bin/perl
use strict;
use warnings;

open my $fh, '<', 'file.txt' or die "Can't open file $!";
my @qa = grep(/\w+/g, split /^(questionstart|answerstart|questionend|answerend)$/mg, do {local $/; <$fh>});
s/^\s+|\s+$//g foreach @qa;

my $flashblock = << "FLASH";
<!-- BEGIN -->
<p class="question">
    $qa[0]
</p>
<p class="answer">
    $qa[1]
</p>
<!-- END -->
FLASH

print $flashblock;

Вывод:

<!-- BEGIN -->
<p class="question">
    hellphellohellohello
</p>
<p class="answer">
    hellohellohello
</p>
<!-- END -->

Если у вас есть несколько пар вопросов / ответов в одном файле, вы можете перебрать массив @qa и напечатать пары или поместить их в хеш и использовать по своему усмотрению.

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