Transforma a tabela em csv e de volta

3

Eu tenho uma mesa

+-------+-------------+------+-----+---------+-------+
| Field | Type        | Null | Key | Default | Extra |
+-------+-------------+------+-----+---------+-------+
| id    | int(11)     | NO   | PRI | NULL    |       |
| foo   | varchar(10) | YES  |     | NULL    |       |
+-------+-------------+------+-----+---------+-------+

Gostaria de definir dois scripts, um que possa transformar essa tabela em csv e um que pode transformá-lo de volta.

Eu sei que esse tipo de possbile com sed

sed -e '1d;3d;$d' -e 's/^|//' -e 's/|$//' -e 's/|/,/g' file

Mas isso não seria confiável.
Uma maneira confiável seria encontrar posições do caractere | na segunda linha (exceto primeiro e último) e, em cada linha, transformar caracteres nessa posição em , e remover os espaços em branco ao redor deles. Isso provavelmente pode ser feito com o awk, mas não sei como.

    
por user1685095 19.07.2017 / 12:26

1 resposta

2

Primeiramente, é um erro usar o caractere pipe para descobrir onde os campos começam e terminam - você já tem uma linha que os define precisamente, sem nenhuma chance de o conteúdo do campo conter o mesmo caractere do separador de campo: primeira linha, que contém apenas marcadores de colunas ( + ) e caracteres de preenchimento ( - ).

Aqui está um script perl ( table-to-csv.pl ) para extrair os dados e imprimi-los como um arquivo formatado csv. Dado que os dados de entrada são uma definição de tabela sql, ele cita todos os campos de dados - uma versão mais genérica pode tentar determinar se a cotação é necessária (ou seja, se o campo é numérico ou não).

Este script é um pouco mais complicado do que realmente precisa ser, por exemplo não há necessidade real de construir os arrays @headers e @data , uma vez que os comprimentos de coluna são conhecidos, cada linha pode ser extraída e impressa à medida que é lida. Assim, é mais fácil executar processamento adicional em um ou ambos os cabeçalhos e os dados, se necessário.

#!/usr/bin/perl -w

use strict;

my @columns = ();
my @headers = ();
my @data = ();

sub extract;  # forward declaration of extract subroutine

# main loop
while(<>) {
  chomp;
  next if (m/^\s*$/);

  if(/^\+-/) {
    # use the '+' chars in the first line to find column positions
    next if (@columns != 0);
    my $i=0;
    while ($i >= 0 && $i < length($_)) {
      my $e=index($_,"+",$i+1);
      # store starting pos & length pair for each column
      push @columns, [ $i+2, $e-3-$i ];
      $i=$e;
    };
    pop @columns;  # last pair will always be bogus, dump it.
  } else {         # extract the headers and data
    if (!@headers) {
      @headers = extract($_,@columns);       # array of field header names
    } else {
      push @data, [ extract($_,@columns) ];  # array of arrays of field data
    };
  };
};

# output in simple csv format.
print join(',',@headers), "\n";

foreach my $l (@data) {
  print join(',',@{ $l }), "\n";
};

### subroutines
sub extract {
  my ($line,@cols) = @_;
  my @f=();

  foreach my $c (@cols) {
    my $d = substr($line,$c->[0],$c->[1]);
    $d =~ s/^\s*|\s*$//g; # strip leading & trailing spaces
    push @f, '"' . $d .'"' ;
  }
  return @f;
};

Saída: (com sua tabela de entrada salva como table.txt)

$ ./table-to-csv.pl table.txt 
"Field","Type","Null","Key","Default","Extra"
"id","int(11)","NO","PRI","NULL",""
"foo","varchar(10)","YES","","NULL",""

A segunda parte da sua pergunta exige a pequena complexidade de criar a matriz @data . É fácil ler e analisar o CSV, especialmente se você usar uma biblioteca como o Text::CSV module do perl para lidar com & quot; campos sem aspas ... mas para obter o formato de saída correto, você precisa de duas passagens pelos dados. O primeiro a encontrar e armazenar a maior largura de cada campo (que é usado para controlar o formato de saída) e o segundo para imprimir os dados.

O seguinte script perl ( csv-to-table.pl ) requer o módulo Text::CSV . Nos sistemas debian etc, está no pacote libtext-csv-perl . Outras distros terão nomes de pacotes semelhantes. Ou você mesmo pode instalá-lo com cpan .

#!/usr/bin/perl -w

use strict;
use Text::CSV;

my @data;
my @lengths;

my $csv = Text::CSV->new ();

while (my $row = $csv->getline(*ARGV)) {
  my @fields = @$row;
  foreach my $i (0..@fields-1) {   # find the largest width for each field
    my $len = length($fields[$i]);
    $lengths[$i] = $len if (!defined($lengths[$i]) || $lengths[$i] <= $len);
  };
  push @data, [ @fields ];  # stuff each record into an array of arrays
};

my $hdr='+';
my $fmt='';

foreach (@lengths) {
  # build the header/separator line and the printf format string
  $hdr .= '-' x ($_+2) . '+';
  $fmt .= '| %-' . ($_) . 's ' ;
};
$fmt .= "|\n";
$hdr .= "\n";

# output the table

print $hdr;
printf "$fmt", @{ $data[0] };
print $hdr;

foreach my $i (1..@data-1) {
  printf $fmt, @{ $data[$i++] };
}

print $hdr;

Saída:

$ ./table-to-csv.pl table.txt  | ./csv-to-table.pl
+-------+-------------+------+-----+---------+-------+
| Field | Type        | Null | Key | Default | Extra |
+-------+-------------+------+-----+---------+-------+
| id    | int(11)     | NO   | PRI | NULL    |       |
| foo   | varchar(10) | YES  |     | NULL    |       |
+-------+-------------+------+-----+---------+-------+
    
por 20.07.2017 / 06:01

Tags