![테이블을 csv로 변환하고 그 반대로 변환](https://linux55.com/image/9702/%ED%85%8C%EC%9D%B4%EB%B8%94%EC%9D%84%20csv%EB%A1%9C%20%EB%B3%80%ED%99%98%ED%95%98%EA%B3%A0%20%EA%B7%B8%20%EB%B0%98%EB%8C%80%EB%A1%9C%20%EB%B3%80%ED%99%98.png)
나한테 테이블이 있어
+-------+-------------+------+-----+---------+-------+
| Field | Type | Null | Key | Default | Extra |
+-------+-------------+------+-----+---------+-------+
| id | int(11) | NO | PRI | NULL | |
| foo | varchar(10) | YES | | NULL | |
+-------+-------------+------+-----+---------+-------+
두 개의 스크립트를 정의하고 싶습니다. 하나는 해당 테이블을 csv로 변환할 수 있고 다른 하나는 다시 변환할 수 있습니다.
나는 sed에 이런 가능성이 있다는 것을 알고 있습니다
sed -e '1d;3d;$d' -e 's/^|//' -e 's/|$//' -e 's/|/,/g' file
그러나 이것은 신뢰할 수 없습니다.
신뢰할 수 있는 방법은 |
두 번째 줄(첫 번째 줄과 마지막 줄 제외)에서 문자 위치를 찾은 다음 각 줄에서 해당 위치의 문자를 문자로 변환 ,
하고 그 주위의 공백을 제거하는 것입니다. 아마 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",""
질문의 두 번째 부분에는 배열 구축에 약간의 복잡성이 필요합니다 @data
. CSV를 읽고 구문 분석하는 것은 쉽습니다. 특히 Text::CSV
Perl 모듈과 같은 라이브러리를 사용하여 인용된 필드와 인용되지 않은 필드를 처리하는 경우... 하지만 올바른 출력 형식을 얻으려면 데이터를 두 번 전달해야 합니다. 첫 번째는 각 필드의 최대 너비(출력 형식을 제어하는 데 사용됨)를 찾아서 저장하고, 두 번째는 데이터를 인쇄합니다.
다음 Perl 스크립트( csv-to-table.pl
)에는 이 Text::CSV
모듈이 필요합니다. 데비안과 같은 시스템에서는 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 | |
+-------+-------------+------+-----+---------+-------+