タブ区切りの表をスペースを入れて整形する

エクセルなどの表計算ソフトで作った表をテキストファイルにコピーペーストしたいときがあります。

f:id:uenewsar:20151226212636p:plain

この表を全選択してコピーして、テキストエディタにペーストします。

すると、セルの間はタブ区切りになるので、表のフォーマットが崩れてしまいます。

f:id:uenewsar:20151226212638p:plain

そこで、タブ区切りの表から、表示が崩れないように整形するperlスクリプトを書きました。

タブ区切りのテキストファイル(in.txt)を標準入力で取り込むと、スペースを入れて整形して、標準出力から出力します(out.txt)。

perl makeSpaceTable.pl < in.txt > out.txt

出力されたテキストファイルは、整形されているので、表っぽく見えます。

f:id:uenewsar:20151226214100p:plain

スクリプト makeSpaceTable.pl

use strict;
use utf8;

# OSにより標準入出力の文字コードを変える
if ($^O =~ /MSWin/) {
    binmode(STDIN, ":encoding(cp932)");
    binmode(STDOUT, ":encoding(cp932)");
    binmode(STDERR, ":encoding(cp932)");
} else {
    binmode(STDIN, ":utf8");
    binmode(STDOUT, ":utf8");
    binmode(STDERR, ":utf8");
}

my @array = ();
my $maxCol = 0;

# 入力文字列をバッファに取り込む
while (<STDIN>) {
    s/[\r\n]+$//; s/\t+$//;
    if ($_ eq "") {
        next;
    }
    my @tab = split(/\t/, $_, 1000);
    push(@array, \@tab);
    # 最大カラム数を保存
    if (@tab>$maxCol) {
        $maxCol = @tab;
    }
}

# バッファの各行のカラム数を合わせる
for (my $i=0; $i<@array; ++$i) {
    for (my $j=0; $j<$maxCol-@{$array[$i]}; ++$j) {
        push(@{$array[$i]}, "");
    }
}

# 各カラムの最大文字長を求める
my @maxLen = ();
for (my $i=0; $i<$maxCol; ++$i) {
    my $tmp = 0;
    for (my $j=0; $j<@array; ++$j) {
        if (getLenForPrint($array[$j][$i]) > $tmp) {
            $tmp = getLenForPrint($array[$j][$i]);
        }
    }
    push(@maxLen, $tmp);
}

# 表示する
for (my $i=0; $i<@array; ++$i) {
    my $buf = "";
    for (my $j=0; $j<$maxCol; ++$j) {
        # カラム間に挿入するスペースを作る
        my $space = " " x ($maxLen[$j] - getLenForPrint($array[$i][$j]));
        if ($i==0 || $j==0) {
            # 第1行、または第1カラムの場合、左詰め
            $buf .= $array[$i][$j] . $space;
        } else {
            # それ以外は右詰め
            $buf .= $space . $array[$i][$j];
        }
        # カラム間にはスペース3個を入れる
        $buf .= "   ";

    }
    # 行末の空白は消しておく
    $buf =~ s/ +$//;
    # 出力
    print $buf . "\n";
}


# 表示される文字長さを数える
# (半角=1, 全角=2になるように)
sub getLenForPrint {

    my ($in) = @_;

    # 半角文字だけを残す
    my $ascii = $in;
    $ascii =~ s/[^\p{InBasicLatin}]//g;

    # 非半角文字だけを残す(→全角文字とみなす)
    my $nonAscii = $in;
    $nonAscii =~ s/[\p{InBasicLatin}]//g;

    return (length($ascii) + length($nonAscii) * 2);

}