awk / perl palíndromo de impressão, se não entre parênteses

0

Eu tenho um arquivo com várias strings, uma em cada linha. Eu quero encontrar e imprimir uma seqüência de caracteres somente se ele contiver um palíndromo e esse palíndromo não estiver entre colchetes. Exemplos:

abba[cdef]gh    # print
abcd[effe]gh    # do not print

Eu tenho atualmente um one-liner que imprime uma linha se a string for um palíndromo

awk 'BEGIN { system("perl -lne \"print if length == 4 && reverse eq \$_\" " ARGV[1]) }' words.txt

modificado de aqui .

Estou pensando em usar sed para remover tudo entre os colchetes e, depois, avaliar o restante para os palíndromos.

Alguma outra ideia sobre como conseguir isso em uma linha?

    
por Jack Baldasso 15.09.2018 / 17:11

3 respostas

1

Para encontrar todos os¹ os palíndromos de 3 ou mais caracteres que não estão dentro de [...] :

$ echo 'cac[ada]abacab' | perl -nle '
    while (/\[.*?\]|(?=(([^][])(?1)|[^][]?))./g) {
      print $1 if length $1 >= 3
    }'
cac
aba
bacab
aca

(observe que ele assume caracteres de byte único, adicione -Mopen=locale para a definição de caracteres do idioma).

O núcleo da correspondência do palíndromo é uma expressão regular recursiva. Um palíndromo é correspondido como uma string vazia ou um único caractere ou um par de caracteres correspondentes com outro palíndromo entre eles. Isso seria ((.)(?1)|.?) , onde (?1) faz a parte recursiva (corresponde ao que está dentro da primeira parte de () , exceto que aqui substituímos . por [^][] (qualquer caractere diferente de ] e [ ).

Ao corresponder todas ocorrências com /.../g , o perl pesquisa a próxima ocorrência após o final da primeira, portanto, se tivéssemos \[.*?\]|(([^][])(?1)|[^][]?) , não encontraríamos bacab in abacab porque primeiro encontraria aba e, em seguida, retomaria a pesquisa após esse aba . Então, aqui, em vez disso, combinamos (?=(palindrome)). , que corresponde a um único caractere ( . ), desde que ele esteja no início de um palíndromo , que é então capturado em $1 . Isso significa que retomaremos a pesquisa após esse único caractere.

¹ Estritamente falando, ele encontra os palíndromes mais longos (com 3 caracteres ou mais) em todas as posições da string, ignorando o [...] s, portanto, ele pode não encontrar todos os ocorrências. Por exemplo, em ababa , encontraria ababa na primeira posição, bab na terceira posição, aba na segunda posição, mas não aba na primeira posição.

    
por 15.09.2018 / 19:52
1

Como sobre um one-liner? Use a palavra entre colchetes como o separador de campo:

perl -F'\[.*?\]' -le 'for $word (@F) {if ($word eq reverse $word) {print; break}}' file

Alguns casos de borda não são considerados aqui:

  • Não analisa o tamanho da string
  • Ele não procura encontrar um palíndromo dentro da palavra: a palavra inteira deve ser um palíndromo.
por 16.09.2018 / 01:51
0

Embora [...] possa ser filtrado antecipadamente, pode ser mais sensato usar um scanner semelhante a lex para percorrer a linha:

#!/usr/bin/env perl
use strict;
use warnings;
LINE: while (readline) {    # for each line (files or stdin)
    LEX: {
        # skip any [] or [...] bits
        redo LEX if m{ \G \[ [^\]]* \] }cgx;
        # two or more not-[ not-vertical-whitespace (\r, \n) chars
        if (m{ \G ([^\[\v]{2,}) }cgx) {
            # palindrome? print the whole line
            if ( $1 eq reverse $1 ) {
                print;
                next LINE;
            }
            # may be more to come...
            redo LEX;
        }
        # advance the lexer a single character
        redo LEX if m{ \G . }cgx;
        # oh it's the end of the line as we know it
    }
}

Que com um caso de teste expandido para várias condições de borda:

% < input
abba[cdef]gh    # print
abcd[effe]gh    # do not print
[effe]f00f
asdf[]prinirp
a[]b[]edgegde
% perl palin < input
abba[cdef]gh    # print
[effe]f00f
asdf[]prinirp
a[]b[]edgegde
% 

Isso pode ser facilmente adaptado para ignorar comentários finais ou outras informações desse tipo.

    
por 15.09.2018 / 20:10