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 | |
+-------+-------------+------+-----+---------+-------+