Como filtrar par de chaves

3

Eu tenho um .toc (arquivo de índice) do meu documento .tex .

Ele contém muitas linhas e algumas delas têm o formato

\contentsline {part}{Some title here\hfil }{5}
\contentsline {chapter}{\numberline {}Person name here}{5}

Eu sei como grep para part e chapter . Mas eu gostaria de filtrar por essas linhas e ter a saída em um arquivo csv como este:

{Some title here},{Person name here},{5}

ou sem chaves

Some title here,Person name here,5

1. Com certeza o número (número da página) no último par {} é o mesmo para as duas linhas, então podemos filtrar apenas o segundo par.

2. Observe que algum par vazio {} pode acontecer ou também pode conter outro par {} . Por exemplo, poderia ser

\contentsline {part}{Title with math $\frac{a}{b}$\hfil }{15}

que deve ser filtrado como

Title with math $\frac{a}{b}$

edit 1: Consegui obter os números sem chaves no final da linha usando

grep '{part}' file.toc | awk -F '[{}]' '{print $(NF-1)}'

edit 2: consegui filtrar as linhas chapter e remover o lixo com

grep '{chapter}' file.toc | sed 's/\numberline//' | sed 's/\contentsline//' | sed 's/{chapter}//' | sed 's/{}//' | sed 's/^ {/{/'

e a saída sem espaços em branco foi

    {Person name here}{5}

edit 3: consegui filtrar por part e limpar a saída com

    \contentsline {chapter}{\numberline {}Person name here}{5}

que retorna

{Title with math $\frac{a}{b}$}{15}
    
por Sigur 06.10.2016 / 22:29

3 respostas

1

Isso está usando o GNU awk , usando POSIX awk seria muito problemático (falta de gensub , que eu uso mais de uma vez).

#!/usr/bin/env gawk

function join(array, result, i)
{
    result = array[0];
    end = length(array) - 1;
    for (i = 1; i <= end; i++)
        result = result "," array[i];
    return result;
}
function push(arr, elem)
{
    arr[length(arr)] = elem;
}

# split("", arr) is a horribly unreadable way to clear an array
BEGIN { split("", arr); }

/{part}|{chapter}/ {
    l = gensub(".*{(.+)}{(.+)}{([0-9]+)}$", "\1,\3,\2", "g");
    if ("part" == substr(l, 0, 4)) {
        if (length(arr) > 0) { print join(arr); }
        split("", arr);
        push(arr, gensub("^(.*),(.*),(.*)$", "\2,\3","g", l));
    } else {
        push(arr, gensub("^(.*),(.*),(.*)$", "\3","g", l));
    }
}

END { print join(arr); }

Isso usa o fato de que as expressões regulares são gananciosas, portanto, as correspondências receberão a linha completa toda vez. Demorou mais esforço do que eu no princípio.

Com a seguinte entrada:

\contentsline {part}{Some title here\hfil }{5}
\contentsline {chapter}{\numberline {}Person name here}{5}
blah blah
\contentsline {chapter}{\numberline {}Person name here}{5}
blah blah
blah blah
\contentsline {chapter}{\numberline {}Person name here}{5}
\contentsline {chapter}{\numberline {}Person name here}{5}
blah blah
blah blah
\contentsline {chapter}{\numberline {}Person name here}{5}
\contentsline {chapter}{\numberline {}Person name here}{5}
\contentsline {part}{Some title here\hfil }{7}
\contentsline {chapter}{\numberline {}Person name here}{7}
blah blah
blah blah
\contentsline {chapter}{\numberline {}Person name here}{7}
blah blah
\contentsline {part}{Some title here\hfil }{9}
blah blah
blah blah
\contentsline {chapter}{\numberline {}Person name here}{9}

Produzimos com cat input | awk -f the_above_script.awk :

5,Some title here\hfil ,\numberline {}Person name here,\numberline {}Person name here,\numberline {}Person name here,\numberline {}Person name here,\numberline {}Person name here,\numberline {}Person name here
7,Some title here\hfil ,\numberline {}Person name here,\numberline {}Person name here
9,Some title here\hfil ,\numberline {}Person name here

O número da página é obtido de {part} e, em seguida, qualquer {chapter} que acontece depois que o {part} é incluído. Isso permite vários capítulos dentro de partes de um livro.

    
por 07.10.2016 / 02:26
1

Com o módulo Perl Text::Balanced , o nível superior {} pode ter seu conteúdo extraído assim:

#!/usr/bin/env perl
use strict;
use warnings;
use Text::Balanced qw(extract_bracketed);

# this will of course fail if the input is one multiple lines, as this
# is only a line-by-line parser of standard input or the filenames
# passed to this script
while ( my $line = readline ) {
    if ( $line =~ m/\contentsline / ) {
        my @parts = extract_contents($line);
        # emit as CSV (though ideally instead use Text::CSV module)
        print join( ",", @parts ), "\n";
    } else {
        #print "NO MATCH ON $line";
    }
}

sub extract_contents {
    my $line = shift;
    my @parts;
    # while we can get a {} bit out of the input line, anywhere in the
    # input line
    while ( my $part = extract_bracketed( $line, '{}', qr/[^{]*/ ) ) {
        # trim off the delimiters
        $part = substr $part, 1, length($part) - 2;
        push @parts, $part;
    }
    return @parts;
}

Com alguma entrada:

% < input 
not content line
\contentsline {chapter}{\numberline {}Person name here}{5}
\contentsline {part}{Title with math $\frac{a}{b}$\hfil }{15}
also not content line
% perl parser input
chapter,\numberline {}Person name here,5
part,Title with math $\frac{a}{b}$\hfil ,15
% 
    
por 07.10.2016 / 02:09
1

Em TXR

@(repeat)
\contentsline {part}{@title\hfil }{@page}
@  (trailer)
@  (skip)
\contentsline {chapter}{\numberline {}@author}{@page}
@  (do (put-line '@title,@author,@page'))
@(end)

Dados da amostra:

\lorem{ipsum}
\contentsline {part}{The Art of The Meringue\hfil }{5}
a
b
c
j
\contentsline {chapter}{\numberline {}Doug LeMonjello}{5}


\contentsline {part}{Parachuting Primer\hfil }{16}

\contentsline {chapter}{\numberline {}Hugo Phirst}{16}

\contentsline {part}{Making Sense of $\frac{a}{b}$\hfil }{19}

\contentsline {part}{War and Peace\hfil }{27}

\contentsline {chapter}{\numberline {}D. Vide}{19}

\contentsline {part}{War and Peace\hfil }{19}

Executar:

$ txr title-auth.txr data
The Art of The Meringue,Doug LeMonjello,5
Parachuting Primer,Hugo Phirst,16
Making Sense of $\frac{a}{b}$,D. Vide,19

Notas:

  • Porque @(trailer) é usado, as linhas que dão ao autor não são tem que seguir estritamente sua parte. Os dados podem apresentar vários elementos \contentsline {part} que são seguidos pelas linhas chapter que correspondem ao número da página.
  • @(skip) implica uma pesquisa em todos os dados restantes. O desempenho pode ser melhorado, limitando o intervalo, adicionando um argumento numérico. Se for possível supor que um {chapter} correspondente seja sempre encontrado em 50 linhas depois de {part} , podemos usar @(skip 50) .
por 07.10.2016 / 17:15

Tags