Converter chave = blocos de valor para CSV

4

Estou tentando transpor um conteúdo de um arquivo para outro.

Arquivo de entrada Test.txt :

HLRSN = 3
IMSI = 404212109727229
KIVALUE = A24AD11812232B47688ADBF15CE05CA9
K4SNO = 1
CARDTYPE = SIM
ALG = COMP128_3

HLRSN = 3
IMSI = 404212109727230
KIVALUE = A24AD11812232B47688ADBF15CE05CB8
K4SNO = 1
CARDTYPE = SIM
ALG = COMP128_3

HLRSN = 3
IMSI = 404212109727231
KIVALUE = A24AD11812232B47688ADBF15CE05CD6
K4SNO = 1
CARDTYPE = SIM
ALG = COMP128_3

Saída necessária em outro arquivo de texto:

3,404212109727229,A24AD11812232B47688ADBF15CE05CA9,1,SIM,COMP128_3
3,404212109727230,A24AD11812232B47688ADBF15CE05CB8,1,SIM,COMP128_3
3,404212109727231,A24AD11812232B47688ADBF15CE05CD6,1,SIM,COMP128_3
    
por user79374 01.08.2014 / 09:04

5 respostas

4

Uma solução bash :

declare -a out

EOF=false
IFS=$'='

until $EOF; do
  read -r skip val || EOF=true
  if [ ! -z "$val" ]
  then
    out+=("${val//[[:space:]]/}")
  else
    tmp="${out[@]}"
    printf '%s\n' "${tmp// /,}"
    out=()
  fi
done < file

Como isso funciona

  • Declare o array out para manter a linha de saída, defina a variável EOF para acompanhar o final do arquivo, IFS para o separador do campo de entrada para read .
  • Até lermos o final do arquivo, lemos cada linha do arquivo, definimos o valor do último campo como variável val .
  • if [ ! -z "$val" ] : verifique se o comprimento da variável $val não é zero, removemos espaço em $val , enviamos para a matriz out .
  • se o comprimento $val for zero, o que significa que obtemos linha em branco ou fim de arquivo, atribuímos todo o elemento da matriz out à variável tmp e substituímos toda a variável de espaço tmp by , , nossa saída projetada recode separador.
  • Defina out como nulo para o próximo trabalho.

Outra solução, mais concisa, mais curta para você é usar perl :

$ perl -F'=' -anle '
    BEGIN { $, = "," }
    push @out,$F[-1] if @F;
    print @{[map {s/\s// && $_} @out]} and @out = ()
        if /^$/ or eof;
' file
3,404212109727229,A24AD11812232B47688ADBF15CE05CA9,1,SIM,COMP128_3
3,404212109727230,A24AD11812232B47688ADBF15CE05CB8,1,SIM,COMP128_3
3,404212109727231,A24AD11812232B47688ADBF15CE05CD6,1,SIM,COMP128_3
    
por 01.08.2014 / 09:48
9

Simplesmente:

awk -v RS= -v OFS=, '{print $3,$6,$9,$12,$15,$18}'

Um separador de registro vazio ( RS= ) ativa o modo de parágrafo pelo qual os registros são separados por seqüências de linhas vazias. Dentro de um registro, aplica-se o separador de campos padrão (os registros são separados por espaços em branco), portanto, em cada registro, os campos nos quais estamos interessados são os 3º, 6º, 9º ...

Alteramos o separador de campo output para um caractere de vírgula ( OFS=, ) e imprimimos os campos nos quais estamos interessados.

    
por 01.08.2014 / 11:16
3

Salve o seguinte em um arquivo (por exemplo, split.awk )

BEGIN {
RS="\n\n";
FS="\n";
ORS=",";
}

{
    for (i=1;i<=NF;i++)
    {
        split($i, sf, "= ")
        print sf[2]
    }
    printf "\n"
 }

Em seguida, execute:

awk -f split.awk Test.txt

Ou execute o comando inteiro como uma linha:

awk 'BEGIN {RS="\n\n";FS="\n";ORS=",";}{for(i=1;i<=NF;i++){split($i, sf, "= ")print sf[2]}printf "\n"}' Test.txt

Funciona da seguinte forma:

  • O bloco BEGIN é executado uma vez no início e define o separador de registro ( RS ) como duas novas linhas e o separador de campo ( FS ) como uma única nova linha. O separador de registro de saída ( ORS ) é definido como uma vírgula.

  • Em seguida, faz um loop em cada campo no registro ( NF é o número de campos no registro atual) e o divide em "=".

  • Em seguida, ele imprime a mão direita desta divisão com uma vírgula entre cada um (o ORS )

  • Após cada linha, é impressa uma nova linha para fornecer o formato CSV.

por 01.08.2014 / 10:27
1

Com base nas outras respostas ... precisei de uma pequena modificação, porque precisava do cabeçalho da coluna no arquivo CSV final. Esta é a minha solução awk:

awk -v RS= -v FS='\n' '
    NR==1 {
      for (i=1; i<=NF; i++) {
        split($i,line,"= "); 
        printf "%s%s", line[1], (i==NF ? "\n" : ",");
      }
    } 
    {
      for (i=1; i<=NF; i++) {
        split($i,line,"= "); 
        printf "%s%s", line[2], (i==NF ? "\n" : ",");
      } 
    }'  

Como nas outras respostas, isso lê o bloco inteiro (até uma linha em branco) nos campos e, em seguida, divide cada campo novamente com split (). A única diferença é que para o primeiro registro, ele imprime a parte antes do '=' e em um segundo loop for, ele imprime o lado direito para todos os registros.

    
por 03.12.2016 / 17:16
0

Se todos os blocos tiverem exatamente o mesmo formato (os mesmos nomes de campo, na mesma ordem), você poderá usar o awk no “modo parágrafo” e imprimir o número do campo desejado em cada bloco. Se sempre houver espaços ao redor dos sinais de igual e os valores nunca contiverem espaços, você poderá confiar nos campos separados por espaço em branco.

awk -v RS= -v ORS=',' '{print $3, $6, $9, $12, $15, $18}'

Se você puder confiar na ordem e na presença de campos, mas não no espaço em branco, precisará de um pouco de análise para dividir nos sinais de igual.

awk -v RS= -F '\n' '{
    for (i = 1; i <= NF; i++) {
        sub(/[^=]*= */, "", $i);
        printf "%s%s", $i, (i==NF ? "\n" : ",");
    }
  }'

Aqui está um método Perl:

perl -000 -ne '
    $, = ","; $\ = "\n";
    @kv = split /\n| *= */;
    print @kv[grep {$_%2} 0..$#kv];
  '

Se os campos nos blocos puderem sair de ordem e você sempre quiser um pedido específico como saída, será necessário armazenar os campos e imprimi-los na ordem correta no final de cada parágrafo. No awk, isso é mais fácil de fazer no modo de linha do que no modo de parágrafo.

awk -v OFS=',' '
    match($0, / *= */) {a[substr($0,1,RSTART-1)] = substr($0,RSTART+RLENGTH)}
    /^$/ {print a["HLRSN"], a["IMSI"], a["KIVALUE"], a["K4SNO"], a["CARDTYPE"], a["ALG"]; split("", a)}
    END {print a["HLRSN"], a["IMSI"], a["KIVALUE"], a["K4SNO"], a["CARDTYPE"], a["ALG"]}
'

Este é um one-liner em Perl.

perl -000 -F'/\n|\s*=\s*/' -ane '%F = @F; $\ = "\n"; $, = ","; print @F{qw(HLRSN IMSI KIVALUE K4SNO CARDTYPE ALG)}'
    
por 02.08.2014 / 02:28