Можно ли объединить два сценария Perl, которые используют разные разделители входных записей - PullRequest
2 голосов
/ 30 марта 2019

Два perl-скрипта, использующие разные разделители входных записей, работают вместе, чтобы преобразовать файл LaTeX во что-то, что легко найти для понятных человеку фраз и предложений.Конечно, они могут быть объединены одним сценарием оболочки.Но мне любопытно, могут ли они быть включены в один скрипт perl.

Причина этих скриптов: было бы непросто найти, например, «два три» внутри short.tex.Но после преобразования grep 'two three' вернет первый абзац.

Для любого файла LaTeX (здесь, short.tex) сценарии вызываются следующим образом.

cat short.tex | try1.pl | try2.pl

try1.pl работает над абзацами.Избавляется от комментариев LaTeX.Он гарантирует, что каждое слово отделено от его соседей одним пробелом, чтобы между словами не скрывались скрытые вкладки, каналы и т. Д.Результирующий абзац занимает одну строку, состоящую из видимых символов, разделенных одиночными пробелами, а в конце - последовательность по крайней мере из двух символов новой строки.

try2.pl удаляет весь файл.Это гарантирует, что параграфы отделены друг от друга ровно двумя новыми строками.И это гарантирует, что последняя строка файла является нетривиальной, содержащей видимые символы.

Можно ли элегантно объединить две операции, подобные этим, которые зависят от разных разделителей входных записей, в один сценарий perl, скажем, big.pl?Например, может ли работа try1.pl и try2.pl быть выполнена двумя функциями или сегментами в квадратных скобках внутри более крупного скрипта?

Кстати, есть ли ключевое слово stackoverflow для «разделителя входных записей»?

try1.pl:

#!/usr/bin/perl
use strict;
use warnings;
use 5.18.2;
local $/ = ""; # input record separator: loop through one paragraph at a time. position marker $ comes only at end of paragraph.
while (<>) {
    s/[\x25].*\n/ /g; # remove all LaTeX comments. They start with % 
    s/[\t\f\r ]+/ /g; # collapse each "run" of whitespace to one single space
    s/^\s*\n/\n/g; # any line that looks blank is converted to a pure newline; 
    s/(.)\n/$1/g; # Any line that does not look blank is joined to the subsequent line
    print;
    print "\n\n"; # make sure each paragraph is separated from its fellows by newlines 
}

try2.pl:

#!/usr/bin/perl
use strict;
use warnings;
use 5.18.2;
local $/ = undef; # input record separator: entire text or file is a single record.
while (<>) {
    s/[\n][\n]+/\n\n/g; # exactly 2 blank lines separate paragraphs. Like cat -s
    s/[\n]+$/\n/; # last line is nontrivial; no blank line at the end
    print;
}

short.tex:

\paragraph{One}
% comment 
two % also 2
three % or 3

% comment
% comment

% comment
% comment

% comment

% comment

So they said%
that they had done it.

% comment
% comment
% comment





Fleas.

% comment

% comment




После преобразования:

\paragraph{One} two three 

So they said that they had done it.

Fleas.

Ответы [ 2 ]

1 голос
/ 07 апреля 2019

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

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

  1. Первый цикл: input = STDIN или файл, переданный в качестве аргумента; разделитель записей = по умолчанию, цикл за строкой; распечатать результат в fileafterperlLIN, временный файл на жестком диске.
  2. Второй цикл: вход = fileafterperlLIN; разделитель записей = "", цикл по абзацу; распечатать результат в fileafterperlPRG, временный файл на жестком диске.
  3. Третий цикл: вход = fileafterperlPRG; разделитель записей = undef, выплескивать весь файл распечатать результат в STDOUT

Это имеет недостаток печати и чтения из двух файлов на жестком диске, что может замедлить его. Преимущества заключаются в том, что для операции требуется только один процесс; и весь код находится в одном файле, что должно облегчить сопровождение.

#!/usr/bin/perl
# 2019v04v05vFriv17h18m41s 
use strict;
use warnings;
use 5.18.2;
my $diagnose;
my $diagnosticstring;
my $exitcode;
my $userName =  $ENV{'LOGNAME'}; 
my $scriptpath; 
my $scriptname; 
my $scriptdirectory;
my $cdld;
my $fileafterperlLIN;
my $fileafterperlPRG;
my $handlefileafterperlLIN;
my $handlefileafterperlPRG;
my $encoding; 
my $count; 
sub diagnosticmessage {
    return unless ( $diagnose );
    print STDERR "$scriptname: ";
    foreach $diagnosticstring (@_) {
        printf STDERR "$diagnosticstring\n";
    }
}

# routine setup 
$scriptpath= $0;
$scriptname=$scriptpath; 
$scriptname=~s|.*\x2f([^\x2f]+)$|$1|;
$cdld="$ENV{'cdld'}"; # a directory to hold temporary files used by scripts
$exitcode=system("test -d $cdld && test -w $cdld || { printf '%\n' 'cdld not a writeable directory'; exit 1; }");
die "$scriptname: system returned exitcode=$exitcode: bail\n" unless $exitcode == 0;
$scriptdirectory="$cdld/$scriptname"; # to hold temporary files used by this script 
$exitcode=system("test -d $scriptdirectory || mkdir $scriptdirectory");
die "$scriptname: system returned exitcode=$exitcode: bail\n" unless $exitcode == 0;
diagnosticmessage ( "scriptdirectory=$scriptdirectory" );
$exitcode=system("test -w $scriptdirectory && test -x $scriptdirectory || exit 1;");
die "$scriptname: system returned exitcode=$exitcode: $scriptdirectory not writeable or not executable. bail\n" unless $exitcode == 0;
$fileafterperlLIN="$scriptdirectory/afterperlLIN.tex";
diagnosticmessage ( "fileafterperlLIN=$fileafterperlLIN" );
$exitcode=system("printf '' > $fileafterperlLIN;");
die "$scriptname: system returned exitcode=$exitcode: bail\n" unless $exitcode == 0;
$fileafterperlPRG="$scriptdirectory/afterperlPRG.tex";
diagnosticmessage ( "fileafterperlPRG=$fileafterperlPRG" );
$exitcode=system("printf '' > $fileafterperlPRG;");
die "$scriptname: system returned exitcode=$exitcode: bail\n" unless $exitcode == 0;

# this script's job: starting with a LaTeX file, which may compile beautifully in pdflatex but be difficult 
# to read visually or search automatically,
# (1) convert any line that looks blank --- a "trivial line", containing only whitespace --- to a pure newline. This is because 
#     (a) LaTeX interprets any whitespace line following a non-blank or "nontrivial" line as end of paragraph, whereas
#     (b) perl needs 2 consecutive newlines to signal end of paragraph. 
# (2) remove all LaTeX comments; 
# (3) deal with the \unskip LaTeX construct, etc.
# The result will be 
# (4) each LaTeX paragraph will occupy a unique line
# (5) exactly one pair of newlines --- visually, one blank line --- will divide each pair of consecutive paragraphs 
# (6) first paragraph will be on first line (no opening blank line) and last paragraph will be on last line (no ending blank line)
# (7) whitespace in output will consist of only 
#     (a) a single space between readable strings, or
#     (b) double newline between paragraphs 
#
$handlefileafterperlLIN = undef;
$handlefileafterperlPRG = undef;
$encoding = ":encoding(UTF-8)";
diagnosticmessage ( "fileafterperlLIN=$fileafterperlLIN" ); 
open($handlefileafterperlLIN, ">> $encoding", $fileafterperlLIN) || die "$0: can't open $fileafterperlLIN for appending: $!";
# Loop 1 / line: 
# default input record separator: loop through one line at a time, delimited by \n
$count = 0;
while (<>) {
    $count = $count + 1;
    diagnosticmessage ( "line $count" ); 
    s/^\s*\n/\n/mg; # convert any trivial line to a pure newline. 
    print $handlefileafterperlLIN $_;
}
close($handlefileafterperlLIN);
open($handlefileafterperlLIN, "< $encoding", $fileafterperlLIN) || die "$0: can't open $fileafterperlLIN for reading: $!";
open($handlefileafterperlPRG, ">> $encoding", $fileafterperlPRG) || die "$0: can't open $fileafterperlPRG for appending: $!";
# Loop PRG / paragraph: 
local $/ = ""; # input record separator: loop through one paragraph at a time. position marker $ comes only at end of paragraph.
$count = 0;
while (<$handlefileafterperlLIN>) {
    $count = $count + 1;
    diagnosticmessage ( "paragraph $count" );
    s/(?<!\x5c)[\x25].*\n/ /g; # remove all LaTeX comments.
   #    They start with % not \% and extend to end of line or newline character. Join to next line.
    #   s/(?<!\x5c)([\x24])/\x2a/g; # 2019v04v01vMonv13h44m09s any $ not preceded by backslash \, replace $ by * or something. 
    #   This would be only if we are going to run detex on the output.
    s/(.)\n/$1 /g; # Any line that has something other than newline, and then a newline, is joined to the subsequent line
    s|([^\x2d])\s*(\x2d\x2d\x2d)([^\x2d])|$1 $2$3|g; # consistent treatment of triple hyphen as em dash
    s|([^\x2d])(\x2d\x2d\x2d)\s*([^\x2d])|$1$2 $3|g; # consistent treatment of triple hyphen as em dash, continued
    s/[\x0b\x09\x0c\x20]+/ /gm; # collapse each "run" of whitespace other than newline, to a single space.
    s/\s*[\x5c]unskip(\x7b\x7d)?\s*(\S)/$2/g; # LaTeX whitespace-collapse across newlines
    s/^\s*//; # Any nontrivial line: No indenting. No whitespace in first column.
    print $handlefileafterperlPRG $_;
    print $handlefileafterperlPRG "\n\n"; # make sure each paragraph ends with 2 newlines, hence at least 1 blank line.
}
close($handlefileafterperlPRG);

open($handlefileafterperlPRG, "< $encoding", $fileafterperlPRG) || die "$0: can't open $fileafterperlPRG for reading: $!";
# Loop slurp 
local $/ = undef;   # input record separator: entire file is a single record.
$count = 0;
while (<$handlefileafterperlPRG>) {
    $count = $count + 1;
    diagnosticmessage ( "slurp $count" );
    s/[\n][\n]+/\n\n/g; # exactly 2 blank lines (newlines) separate paragraphs. Like cat -s
    s/[\n]+$/\n/;           # last line is visible or "nontrivial"; no trivial (blank) line at the end
    s/^[\n]+//;             # no trivial (blank) line at the start. The first line is "nontrivial."
    print STDOUT;
}
1 голос
/ 30 марта 2019

Чтобы объединить try1.pl и try2.pl в один скрипт, вы можете попробовать:

local $/ = "";
my @lines;
while (<>) {
    [...]    # Same code as in try1.pl except print statements
    push @lines, $_;
}

$lines[-1] =~ s/\n+$/\n/;
print for @lines;
...