Как напечатать на одной строке весь контент между определенными линиями старта и стопа? - PullRequest
3 голосов
/ 03 декабря 2011
while(<FILE>)
{
    chomp $_;
    $line[$i]=$_;
    ++$i;
}

for($j=0;$j<$i;++$j)
{
    if($line[$j]=~/Syn_Name/)
    {
        do
        {
            print OUT $line[$j],"\n";
            ++$j;
        }
        until($line[$j]=~/^\s*$/)
    }
}

Это мой код, я пытаюсь распечатать данные между Syn_Name и пустой строкой.Мой код извлекает тот кусок, который мне нужен.Но данные между чанками печатаются построчно.Я хочу, чтобы данные для каждого блока печатались в одной строке.

Ответы [ 3 ]

6 голосов
/ 03 декабря 2011

Упрощение вашего кода. Использование оператора триггера для управления печатью. Обратите внимание, что печать последней строки не добавит новую строку (если строка не содержит более одной новой строки). В лучшем случае он печатает пустую строку. В худшем случае печатает пробел.

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

#chomp(my @line = <FILE>);
while (<FILE>) {
    chomp;
    if(/Syn_Name/ .. /^\s*$/) {
        print OUT;
        print "\n" if /^\s*$/;
    }
}
5 голосов
/ 03 декабря 2011

Содержание

  • Идиоматический Perl
  • Упрощение исправления ошибок
    • Предупреждения об общих ошибках программирования
    • Не выполнять, если только имена переменных
    • Развитие этой привычки сэкономит вам много времени
  • Оператор диапазона Perl
  • Рабочие демонстрации
    • Печатайте сжатые строки сразу
    • Объединение строк с пробелами
    • Еще один крайний регистр

Идиоматический Perl

У вас, кажется, естьфон с семейством языков C.Это хорошо, потому что он выполняет свою работу, но вы можете позволить Perl обрабатывать оборудование для вас, а именно:

  • chomp по умолчанию $_ (также верно для многихдругие операторы Perl)
  • push добавляет элемент в конец массива

, чтобы упростить ваш первый цикл:

while (<FILE>)
{
    chomp;
    push @line, $_;
}

Теперь у вас нет обновления $i для отслеживания того, сколько строк вы уже добавили в массив.

Во втором цикле вместо использования цикла for в стиле Cиспользуйте цикл foreach:

Цикл foreach выполняет итерацию по обычному значению списка и устанавливает переменную VAR в качестве каждого элемента спискав свою очередь…

Ключевое слово foreach фактически является синонимом для ключевого слова for, поэтому вы можете использовать foreach для удобства чтения или for для краткости.(Или потому, что оболочка Bourne вам более знакома, чем csh, поэтому запись для for происходит более естественно.) Если VAR опущено, $_ устанавливается на каждое значение.

Таким образом, Perl обрабатывает бухгалтерию для вас.

for (@line)
{
    # $_ is the current element of @line
    ...
}

Упрощает исправление ошибок

Иногда Perl может быть слишком приспособлен.Скажем, во втором цикле вы допустили простую опечатку:

for (@lines)

Запуск вашей программы теперь вообще не выводит, даже если вход содержит куски Syn_Name.

Человек может посмотреть накод и видите, что вы, вероятно, намеревались обработать массив, который вы только что создали, и по ошибке присвоили имя массива.Perl, стремясь помочь, создает новый пустой массив @lines, в результате чего ваш цикл foreach не имеет ничего общего.

Вы можете удалить ложный s в конце имени массива, ноеще есть программа не выдает выходной!Например, у вас может быть необработанная комбинация входных данных, которая не открывает файловый дескриптор OUT.

В Perl есть несколько простых способов избавить вас от этих (и более!) Разочарований от работы стихие сбои.

Предупреждения об общих ошибках программирования

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

Name "main::lines" used only once: possible typo at ./synname line 16.

и после исправления опечатки в имени массива

print() on unopened filehandle OUT at ./synname line 20, <FILE> line 8.
print() on unopened filehandle OUT at ./synname line 20, <FILE> line 8.
print() on unopened filehandle OUT at ./synname line 20, <FILE> line 8.
print() on unopened filehandle OUT at ./synname line 20, <FILE> line 8.
print() on unopened filehandle OUT at ./synname line 20, <FILE> line 8.

сразу же вы увидите ценную информацию, которая можетбыть трудным или, по крайней мере, утомительным для выявления без посторонней помощи:

  1. имена переменных не согласованы, и
  2. программа пытается произвести вывод, но требует немного больше сланцев.

Не выполнять, если имена переменных не согласованы

Обратите внимание, что даже с учетом потенциальных проблем, описанных выше, Perl все равно пытался выполнить.В некоторых классах проблем, таких как несоответствие имен переменных, вы можете предпочесть, чтобы Perl не выполнял вашу программу, а остановился и заставил вас сначала исправить ее.Вы можете указать Perl, чтобы он был строгим в отношении переменных :

Это генерирует ошибку во время компиляции, если вы обращаетесь к переменной, которая не была объявлена ​​через our или use vars, локализован через my, или не был полностью квалифицирован.

Компромисс заключается в том, что вы должны четко указывать, какие переменные вы намереваетесь стать частью вашей программы, вместо того, чтобы позволять им удобно прыгать вжизнь при первом использовании.Перед первым циклом вы должны объявить

my @line;

чтобы выразить свое намерение.Затем из-за ошибки ошибочно множественного имени массива Perl завершается с ошибкой

Global symbol "@lines" requires explicit package name at ./synname line 16.
Execution of ./synname aborted due to compilation errors.

, и вы точно знаете, в какой строке содержится ошибка.

Разработка этой привычки сэкономит вам много времени

Я начинаю почти каждую нетривиальную программу на Perl, которую я пишу

#! /usr/bin/env perl

use strict;
use warnings;

Первая - это строка Шебанга, обычный комментарий для Perl.Строки use активируют прагму strict и прагму warnings.

Не желая быть строгим зомби Как отметил Марк Доминус, я укажу, что use strict;, как указано выше без опций, делает Perl строгим в отношении трех подверженных ошибкам областей:

  1. строгих переменных,как описано выше;
  2. строгие ссылки, запрещает использование символических ссылок;и
  3. строгие подпрограммы, требует, чтобы программист был более осторожен в обращении к подпрограммам.

Это очень полезное значение по умолчанию.См. документацию strict прагмы для получения более подробной информации.

Оператор диапазона Perl

Документация perlop описывает .., Perl'sоператор диапазона , который может помочь вам значительно упростить логику во втором цикле:

В скалярном контексте .. возвращает логическое значение.Оператор является бистабильным, как триггер, и эмулирует оператор диапазона строк (запятая) sed , awk и различных редакторов.Каждый оператор .. поддерживает свое собственное логическое состояние, даже при вызовах подпрограммы, которая его содержит.Это ложно, пока его левый операнд ложен.Если левый операнд равен true, оператор диапазона остается истинным, пока правый операнд не станет true, ПОСЛЕ , и оператор диапазона снова становится ложным.Он не становится ложным, пока в следующий раз не будет оценен оператор диапазона.

В своем вопросе вы написали, что хотите «данные между Syn_Name и пустой строкой», которые пишутся в Perl

/Syn_Name/ .. /^\s*$/

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

К последнему порядковому номеру в диапазоне добавлена ​​строка "E0", которая не влияет на его числовое значение, но дает вам возможность искать, если вы хотите исключить конечную точку.

Назначение значения, возвращенного из .. (что я обычно делаю для скаляра с именем $inside или $is_inside), позволяет вам проверить, находитесь ли вы в конце, например ,

my $is_inside = /Syn_Name/ .. /^\s*$/;
if ($is_inside =~ /E0$/) {
    ...
}

Написание этого текста также позволяет избежать дублирования кода для вашего условия завершения (правый операнд ..).Таким образом, если вам нужно изменить логику, вы измените ее только в одном месте.Когда вы должны помнить, вы иногда будете забывать и создавать ошибки.

Рабочие демонстрации

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

Немедленно печатать разбитые строки

Как определено в вашем вопросе, нет необходимости в одном цикле для сбора строкво временном массиве, а затем еще один цикл для обработки массива.Рассмотрим следующий код

#! /usr/bin/env perl

use strict;
use warnings;

# for demo only
*FILE = *DATA;
*OUT = *STDOUT;

while (<FILE>)
{
    chomp;
    if (my $is_inside = /Syn_Name/ .. /^\s*$/) {
        my $is_last = $is_inside =~ /E0$/;
        print OUT $_, $is_last ? "\n" : ();
    }
}

__DATA__
ERROR IF PRESENT IN OUTPUT!

Syn_Name
foo
bar
baz

ERROR IF PRESENT IN OUTPUT!

с выходным значением

Syn_Namefoobarbaz

Мы всегда печатаем текущую строку, сохраненную в $_.Когда мы находимся в конце диапазона, то есть когда $is_last равно true, мы также печатаем новую строку.Когда $is_last равно false, в другой ветви тернарного оператора получается пустой список, то есть мы печатаем только $_, без новой строки.

Объединяем строки с пробелами

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

#! /usr/bin/env perl

use strict;
use warnings;

# for demo only
*FILE = *DATA;
*OUT = *STDOUT;

my @lines;
while (<FILE>)
{
    chomp;
    if (my $is_inside = /Syn_Name/ .. /^\s*$/) {
        push @lines, $_;
        if ($is_inside =~ /E0$/) {
            print OUT join(" ", @lines), "\n";
            @lines = ();
        }
    }
}

__DATA__
ERROR IF PRESENT IN OUTPUT!

Syn_Name
foo
bar
baz

ERROR IF PRESENT IN OUTPUT!

Этот код накапливает в @lines только те строки в чанке Syn_Name, печатает чанк и очищает @lines, когда мы видим терминатор. Выход сейчас

Syn_Name foo bar baz

Еще один крайний футляр

Наконец, что произойдет, если мы увидим Syn_Name в конце файла, но без завершающей пустой строки? Это может быть невозможно с вашими данными, но в случае, если вам нужно обработать их, вы захотите использовать оператор eof в Perl .

eof FILEHANDLE
ВФ

Возвращает 1, если следующее чтение в FILEHANDLE вернет конец файла или если FILEHANDLE не открыто & hellip; eof без аргумента использует последний прочитанный файл.

Таким образом, мы заканчиваем либо пустой строкой, либо концом файла.

#! /usr/bin/env perl

use strict;
use warnings;

# for demo only
*FILE = *DATA;
*OUT = *STDOUT;

my @lines;
while (<FILE>)
{
    s/\s+$//;
    #if (my $is_inside = /Syn_Name/ .. /^\s*$/) {
    if (my $is_inside = /Syn_Name/ .. /^\s*$/ || eof) {
        push @lines, $_;
        if ($is_inside =~ /E0$/) {
            print OUT join(" ", @lines), "\n";
            @lines = ();
        }
    }
}

__DATA__
ERROR IF PRESENT IN OUTPUT!
Syn_Name
foo
bar

YOU CANT SEE ME!
Syn_Name
quux
potrzebie

Выход:

Syn_Name foo bar 
Syn_Name quux potrzebie

Здесь вместо chomp код удаляет все невидимые пробелы в конце строк. Это обеспечит равномерный интервал между соединенными линиями, даже если вход немного неаккуратный.

Без проверки eof программа не печатает последнюю строку, которую можно увидеть, закомментировав активное условное выражение и раскомментировав другое.

0 голосов
/ 03 декабря 2011

Еще одна упрощенная версия:

foreach (grep {chomp; /Syn_Name/ .. /^\s*$/ } <FILE>) {
    print OUT;
    print OUT "\n" if /^\s*$/;
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...