Детали обработки, по-видимому, сложны; давайте сначала разберемся, как обрабатывать блоки.
Один из способов - построчно и накапливать строки для блока, пока не дойдете до пустой строки. Затем вы обрабатываете свой блок, очищаете буфер и продолжаете. Например
use warnings;
use strict;
use feature 'say';
sub process_block {
say "Block:"; say "\t$_" for @{$_[0]};
}
my $file = shift // die "Usage: $0 filename\n"; #/
open my $fh, '<', $file or die "Can't open $file: $!";
my @block;
while (<$fh>) {
chomp;
if (not /\S/) {
if (@block) { # the first empty line
process_block(\@block);
@block = ();
}
next;
}
push @block, $_;
}
process_block(\@block) if @block; # last block may have remained
Вызов process_block
после цикла while
не срабатывает для показанного примера, поскольку перед концом файла есть пустые строки, поэтому последний блок обрабатывается внутри цикла. Но нам нужно убедиться, что последний блок обрабатывается, когда в конце также нет пустых строк.
Внутри process_block
теперь вы можете проверить, содержит ли @block
mrb@#Name
, применить другие (очевидно сложные) условия, выполнить регулярное выражение и напечатать обработанные строки.
Вот пример, следующий за разъяснениями, но все же оставляющий некоторые детали
use List::Util qw(any); # used to be in List::MoreUtils
sub process_block {
my @block = @{ $_[0] }; # local copy, to not change @block in caller
if ($block[0] =~ /^txt\@/ and any { /^mrb\@/ } @block) {
for (@block) {
s{^txt\@#(\S*)\s+(.*)}
{\\ex[exno=\\spkr{$1}, exnoformat=X] \\begingl \\glpreamble $2 // }g; #/
s{^mrb\@#\S*\s+(.*)}{\\gla $1 // }g;
# etc
}
if ($block[-1] =~ /^\s*SD/) {
my $SD_line = pop @block;
push @block, '\endgl', $SD_line, '\xe';
}
else {
push @block, '\endgl', '\xe';
}
}
else {
for (@block) {
s/^txt\@#(\S*)\s+(.*)/\\ex[exno=\\spkr{$1}, exnoformat=X] \\txt $2 /g;
s/^tli\@#\S*\s+(.*)/\\translit $1 /g;
# etc
}
push @block, '\xe';
}
say for @block;
say "\n"; # two lines to separate blocks
}
Примечание по эффективности.
Этот код обрабатывает каждую строку в блоке против всех подстановок регулярных выражений, чтобы найти ту, которая применяется к ней. Отличительная схема появляется в самом начале, поэтому «неправильные» строки сразу перестают работать, но мы по-прежнему запускаем механизм регулярных выражений для всех проверок для каждой строки.
Это может (или не может) быть проблемой со многими регулярными выражениями или длинными блоками, или если это делается часто, и его можно оптимизировать, если он медленный. Поскольку список подстановок всегда один и тот же, мы можем создать хеш с регулярным выражением, определяемым отличительным началом шаблона (как таблица отправки ). Например
my %repl_non_mrb = (
'txt@' => sub { s/^txt\@#(\S*)\s+(.*)/\\ex[exno=\\spkr{$1}, exnoformat=X] \\txt $2 /g }
'tli@' => sub { s/^tli\@#\S*\s+(.*)/\\translit $1 /g },
...
);
my %repl_mrb = ( ... );
, а затем используйте его в соответствии с
# For blocks without 'mrb@'
for (@block) {
# Capture key: up to # for 'txt@' (etc), up to \s for 'SD'. Other cases?
my ($key) = /^(.*?)(?:#|\s)/;
if ($key and exists $repl_non_mrb{$key}) {
$repl_non_mrb{$key}->(); # run the coderef
}
else { say "No processing key (?) for: $_" } # some error?
}
Это явно требует более (осторожной) работы, в то время как есть и другие способы организовать это регулярное выражение. Но реализация этих (фиксированных) замен регулярных выражений, хэшированных их отличительными шаблонами, несомненно, улучшит сложность O (NM) , заключающуюся в том, чтобы всегда выполнять все регулярные выражения в каждой строке.
Другой способ, о чем вы спрашиваете
Я знаю, что Perl может делать блок за блоком
что можно сделать, установив переменную $/
. Он устанавливает то, что затем используется в качестве разделителя между входными записями. Если вы установите его на \n\n
, здесь вы получите блок, обслуживаемый для каждого чтения, в строке
open my $fh, '<', $file or die "Can't open $file: $!";
PROCESS_FILE: {
local $/ = "\n\n";
while (my $block = <$fh>) {
chomp $block;
say "|$block|";
}
};
Я поместил это в блок (названный PROCESS_FILE
просто так), чтобы мы могли изменить $/
, используя local . Затем его предыдущее значение восстанавливается при выходе из блока, и файлы снова читаются нормально.
Однако я не вижу в этом преимущества, поскольку у вас теперь есть блок в скалярной переменной, в то время как то, что вам нужно сделать, кажется ориентированным на строки. Поэтому я бы порекомендовал первый подход.