Como ordenar com um delimitador com vários caracteres de comprimento?

5

ENTRADA:

$ cat a.txt 
1FOO2FOO3
4FOO5FOO5
2FOO1FOO9
$ 

OUTPUT:

$ cat a.txt | sort SOMEMAGIC
2FOO1FOO9
1FOO2FOO3
4FOO5FOO5
$ 

Pergunta : Como posso classificar, se eu tiver um delimitador longo de vários caracteres? ("FOO")?

No exemplo a.txt é classificado pela segunda coluna.

A questão é que, em geral, os números em a.txt podem ser qualquer coisa.

    
por evachristine 04.07.2014 / 20:05

4 respostas

5

Use por exemplo sed para substituir a cadeia por um delimitador de um caractere, classificar pela coluna e, em seguida, substituir novamente o delimitador:

sed -e s/FOO/X/g a.txt | sort -k 2,2 -t X | sed -e s/X/FOO/g 

Isso pressupõe que há um caractere que você sabe que não aparece na entrada. Um caractere de controle seria um candidato comum, mas você precisa fazer uma escolha com base no seu conhecimento do formato de entrada.

    
por 04.07.2014 / 20:19
4

Tente isto:

$ perl -ane '
    push @h,[$_,(split(/FOO/))[1]];
    END {
        print map  { $_->[0] }
              sort {$a->[1] <=> $b->[1]}
              @h;
    }
' file
2FOO1FOO9
1FOO2FOO3
4FOO5FOO5

Explicação

  • Armazene cada matriz ref [linha, chave] na matriz @h : [$_,(split(/FOO/))[1]]

  • Quando terminar de ler o arquivo:

    • Classifique a matriz ref na matriz @h pela chave sort {$a->[1] <=> $b->[1]}
    • Extraia a linha original de @h e imprima map { $_->[0] }
por 04.07.2014 / 20:51
2

Supondo que seus campos sejam numéricos, o GNU sort 's classificação de versões pode ajudar. Defina o delimitador como F para que o% deOO de FOO à direita entre no campo 2 e o campo 2 seja visto por sort como contendo valores OO2, OO5 and OO1 . Especificar a classificação de versão no campo 2 garante que o prefixo não numérico OO seja ignorado e a saída seja ordenada pelas partes numéricas finais do campo 2

sort -k2,2V -t 'F' a.txt
2FOO1FOO9
1FOO2FOO3
4FOO5FOO5

Alternativamente com o GNU awk , se você tolerar a sobrecarga de uma solução na memória:

awk -F'FOO' '{a[$2]=$0};END{asort(a, b, "@ind_num_asc");
for (i in b) print b[i]}' a.txt
2FOO1FOO9
1FOO2FOO3
4FOO5FOO5
    
por 05.07.2014 / 02:56
1

Acho que você não precisa se concentrar tanto no delimitador quanto na sort -k eys. Você pode especificar um intervalo de caracteres para suas chaves.

info sort

...given the input line ' foo bar', sort breaks it into fields ' foo' and ' bar'. The field separator is not considered to be part of either the field preceding or the field following, so with 'sort -t " "' the same input line has three fields: an empty field, 'foo', and 'bar'. However, fields that extend to the end of the line, as '-k 2', or fields consisting of a range, as '-k 2,3', retain the field separators present between the endpoints of the range.

E como Stephane comentou, isso também se aplica vice-versa - você pode classificar apenas uma fatia de um campo com um intervalo de bytes e até mesmo várias fatias do mesmo campo com várias chaves. Portanto, no seu caso, você pode usar o mesmo campo com intervalos diferentes várias vezes. Vêem?

sort -k1.5n -k1.1n --debug <<\DATA
1FOO2FOO3
4FOO5FOO5
2FOO1FOO9
DATA
sort: using simple byte comparison
sort: leading blanks are significant in key 1; consider also specifying 'b'
sort: key 1 is numeric and spans multiple fields
sort: key 2 is numeric and spans multiple fields
2FOO1FOO9
    _
_
_________
1FOO2FOO3
    _
_
_________
4FOO5FOO5
    _
_
_________

Isso instrui sort a ordenar a chave primária começando com o 5º byte no campo 1 e se estendendo até o final do campo 1, e secundariamente para classificar do 1o byte no campo 1 para o campo de um fim. A opção --debug é muito útil ao tentar classificar sort , como espero que o acima demonstre. Mas aqui está sem depuração:

sort -k1.5n -k1.1n <<\DATA        
1FOO2FOO3
4FOO5FOO5
2FOO1FOO9
DATA

###OUTPUT###
2FOO1FOO9
1FOO2FOO3
4FOO5FOO5

Se você quisesse sort apenas um único caractere para cada um, seria necessário fechar seus intervalos. No exemplo acima, -k1.5n funciona do byte 5 para o final do campo porque a especificação da chave funciona como:

 -k[begin field].[first byte in key],[end field].[last byte in key]

Assim, embora os resultados sejam idênticos para este caso, você pode fechar cada intervalo de campo no mesmo byte com o qual você o abre:

sort -k1.5,1.5n -k1.1,1.1n

e, portanto, apenas sort por um único byte para cada chave.

    
por 06.07.2014 / 03:29

Tags