テーブルをcsvに変換し、その逆に変換

テーブルをcsvに変換し、その逆に変換

私にテーブルがあります。

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

2つのスクリプトを定義したいと思います。 1つはそのテーブルをcsvに変換でき、もう1つは再変換できます。

私はsedにこの可能性があることを知っています

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

しかし、これは信頼できません。
信頼できる方法は、|2行目(最初の行と最後の行を除く)で文字位置を見つけて、各行でその位置の文字を文字に変換して,その周囲のスペースを削除することです。おそらくawkで行うことができますが、どうすればいいかわかりません。

答え1

まず、パイプ文字を使用してフィールドの始まりと終わりが間違った場所を見つけます。すでにこれを正確に定義する行があり、フィールドの内容にフィールド区切り文字と同じ文字が含まれる可能性はありません。ただ列マーカー(+)と塗りつぶし文字(-)。

table-to-csv.plデータを抽出してcsv形式のファイルに印刷するPerlスクリプト()。入力データがSQLテーブル定義であると仮定すると、各データフィールドが参照されます。より一般的なバージョンでは、参照が必要かどうか(たとえば、フィールドが数値であるかどうか)を確認しようとすることができます。

このスクリプトは必要以上に少し複雑です。たとえば、列の長さがわかったら、各行を読み取るときに抽出して印刷できるので、実際にビルドして@headers配置する必要はありません。@dataこれにより、必要に応じてヘッダーとデータの一方または両方に対する追加処理を簡単に実行できます。

#!/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;
};

出力:(入力テーブルを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",""

質問の2番目の部分には、配列の構築に若干の複雑さが必要です@data。 CSVを読んで解析するのは簡単です。特に、Text::CSVPerlモジュールなどのライブラリを使用して引用符付きフィールドと引用符付きフィールドを処理する場合...しかし、正しい出力形式を取得するにはデータを2回渡す必要があります。 1つ目は各フィールドの最大幅(出力形式を制御するために使用されます)を見つけて保存し、2つ目はデータを印刷します。

次のPerlスクリプト(csv-to-table.pl)にはこのText::CSVモジュールが必要です。 Debian のようなシステムはlibtext-csv-perlパッケージにあります。他のディストリビューションも同様のパッケージ名を持っています。または、独自のインストールを使用することもできます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;

出力:

$ ./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    |       |
+-------+-------------+------+-----+---------+-------+

関連情報