Chinaunix

标题: 将文本格式转换成表格形式(使用等宽字体显示) [打印本页]

作者: Perlvim    时间: 2013-03-31 15:46
标题: 将文本格式转换成表格形式(使用等宽字体显示)
本帖最后由 Perlvim 于 2013-03-31 15:49 编辑

一个由二个空格以上分隔的列
colname1   colname2   colname3
str1       str2      str3
str4       str5      str6

转换成:

------------------------------
colname1 | colname2 | colname3
------------------------------
str1        | str2     | str3
------------------------------
str4        | str5     | str6
------------------------------


或者其中有一个单元格为空

colname1   colname2   colname3
str1                  str3
str4       str5       str6

转换成:

------------------------------
colname1 | colname2 | colname3
------------------------------
str1        |                | str3
------------------------------
str4        | str5         | str6
------------------------------

其中一个单元格有换行
colname1   colname2   colname3
str1       str2       str3 str3
                      str3
str4       str5       str6

-------------------------------
colname1 | colname2 | colname3
-----------------------------
str1        | str2           | str3 str3
              |                  | str3
-----------------------------
str4        | str5           | str6
-----------------------------

作者: yinyuemi    时间: 2013-03-31 15:47
  1. #!/usr/bin/env perl

  2. use strict;
  3. use Data::Dumper;
  4. my %data_v;
  5. my $max_len;
  6. my ($line,$col);
  7. while(<DATA>){
  8.         chomp;
  9.         $line++;
  10.         $col = 0;
  11.         map{
  12.                 push @{$data_v{$line}},$_;
  13.                 $col++;
  14.                 $max_len = length($_) if $max_len <= length($_);
  15.         }split; # use your defined delimited character, like "\t", to separate the line;
  16. }

  17. my $ps = $max_len * $col + ($col - 1)*3;
  18. foreach my $x (1..$line){
  19.                 print "|-" . '-' x $ps . "|\n| ";
  20.                 print join " | ", map{$max_len == length($_) ? $_ : $_ . ' ' x ($max_len - length($_))}@{$data_v{$x}};
  21.                 print "|\n";
  22. }
  23. print "|-" . '-' x $ps . "|\n";
  24. __DATA__
  25. col1                 col2                  col3                         col4
  26. Abidjan      Banjul       Conakry        Harare
  27. Accra        Bissau       Dakar          Johannesburg
  28. Addis_Ababa  Blantyre     Dar_es_Salaam  Juba
  29. Algiers      Brazzaville  Djibouti       Kampala
复制代码
回复 12# Perlvim


   
作者: rubyish    时间: 2013-03-31 17:03
如何不一样?
  1. 其中一个单元格有换行
  2. colname1   colname2   colname3
  3. str1       str2       str3 str3
  4.                       str3
  5. str4       str5       str6
复制代码
  1. AAA      BBB      CCC
  2. a1        b1       c1c2
  3. empty   empty  c2
  4. a2        b2       c2
复制代码

作者: Perlvim    时间: 2013-03-31 18:26
回复 2# rubyish
有换行,还是一个单元格,前两个都为空,就是单独的单元格了。


   
作者: zhlong8    时间: 2013-03-31 19:23
这不是 format 干的事吗?不过我从来没学会啊,还有相关的那几个特殊变量
作者: Perlvim    时间: 2013-03-31 21:16
回复 4# zhlong8

format 不是程序员做的事情,简直就是文员做的事情,比 word 还麻烦,根本没有程序的特性。

   
作者: rubyish    时间: 2013-04-01 09:43
本帖最后由 rubyish 于 2013-04-01 05:46 编辑

是 ma ?
  1. #!/usr/bin/perl
  2. sub table {
  3.     my %i = ( @_, @_ % 2 ? 0 : () );
  4.     my @dat = @{ $i{data} || shift };
  5.     my $p = $i{align} // 0;
  6.     my %T   = (
  7.         t => [ qw(┌ ┬ ┐) ],
  8.         m => [ qw(├ ┼ ┤) ],
  9.         b => [ qw(└ ┴ ┘) ],
  10.         h => '─', v => '│'
  11.     );
  12.     my $l = 0;
  13.     map $l < $_ && ( $l = $_ ), map length, map @$_, @dat;
  14.     my $e = scalar @{ $dat[0] };
  15.     my ( $ht, $hm, $hb ) = map {
  16.         $T{$_}[0] . join( $T{$_}[1], ( $T{h} x ( $l + 2 ) ) x $e ) . "$T{$_}[2]\n";
  17.     } qw/t m b/;
  18.     my $al = sub { map { my $s = $" x ( 1 + $l - length ); $p ? "$s$_ " : " $_$s" } @_ };
  19.     my $text = sub { $T{v} . join( $T{v}, $al->(@_) ) . "$T{v}\n" };
  20.     $ht . join( $hm, map $text->(@$_), @dat ) . $hb;
  21. }

  22. my @a = map [split], <DATA>;

  23. print table \@a;
  24. print table data => \@a, align => 1;
  25. print table data => \@a';

  26. __DATA__
  27. Abidjan      Banjul       Conakry        Harare
  28. Accra        Bissau       Dakar          Johannesburg
  29. Addis_Ababa  Blantyre     Dar_es_Salaam  Juba
  30. Algiers      Brazzaville  Djibouti       Kampala
复制代码

作者: dahe_1984    时间: 2013-04-01 11:04
其中有单元格和回车只能是肉眼判断啊? 那程序也无能为力呀
作者: dahe_1984    时间: 2013-04-01 11:06
回复 6# rubyish

呵呵,您写的代码永远这么高深莫测,我总是看不懂


   
作者: Perlvim    时间: 2013-04-01 11:10
本帖最后由 Perlvim 于 2013-04-01 11:11 编辑

好好研究中。。
  1. ┌───────┬───────┬───────┬───────┐
  2. │ Abidjan      │ Banjul       │ Conakry      │ Harare       │
  3. ├───────┼───────┼───────┼───────┤
  4. │ Accra        │ Bissau       │ Dakar        │ Johannesburg │
  5. ├───────┼───────┼───────┼───────┤
  6. │ Addis_Ababa  │ Blantyre     │Dar_es_Salaam │ Juba         │
  7. ├───────┼───────┼───────┼───────┤
  8. │ Algiers      │ Brazzaville  │ Djibouti     │ Kampala      │
  9. └───────┴───────┴───────┴───────┘
复制代码

作者: Perlvim    时间: 2013-04-01 11:45
本帖最后由 Perlvim 于 2013-04-01 11:50 编辑

代码翻译一下,代码思维极具跳跃性,我也是基本靠猜。
  1. #!/usr/bin/perl
  2. sub table {
  3.     # 传递参数,如果只有一个参数,就设置为默认对齐方式
  4.     my %i = ( @_, @_ % 2 ? 0 : () );
  5.     # 如果有 data 就取其作为数据项
  6.     my @dat = @{ $i{data} || shift };
  7.     # 如果有 align 就取其作为对齐参数
  8.     my $p = $i{align} // 0;
  9.     # 边框字符表
  10.     my %T   = (
  11.         t => [ qw(┌ ┬ ┐) ],
  12.         m => [ qw(├ ┼ ┤) ],
  13.         b => [ qw(└ ┴ ┘) ],
  14.         h => '─',
  15.         v => '│',
  16.     );
  17.     # 最长的字段长度
  18.     my $l = 0;
  19.     # 典型施瓦茨变换格式
  20.     # 获取最长字段的数值
  21.     map $l < $_ && ( $l = $_ ),
  22.     # 获取长度
  23.     map length,
  24.     # 解引用
  25.     map @$_, @dat;
  26.     # 获取字段数量
  27.     my $e = scalar @{ $dat[0] };
  28.     # 生成表格框架
  29.     # 开头字符 + 分界符 + 字段最长长度 + 2个空格 + 分界符 + 末尾字符
  30.     my ( $ht, $hm, $hb ) = map {
  31.         $T{$_}[0] . join( $T{$_}[1], ( $T{h} x ( $l + 2 ) ) x $e ) . "$T{$_}[2]\n";
  32.     } qw/t m b/;
  33.     # 生成将数据按照长度插入分隔符的子程序
  34.     my $al = sub { map { my $s = $" x ( 1 + $l - length ); $p ? "$s$_ " : " $_$s" } @_ };
  35.     # 生成文本解析子程序
  36.     my $text = sub { $T{v} . join( $T{v}, $al->(@_) ) . "$T{v}\n" };
  37.     # 增加前后表头,并生成最终结果
  38.     $ht . join( $hm, map $text->(@$_), @dat ) . $hb;
  39. }

  40. # 将内容保存为数组的引用的数组
  41. my @a = map [split], <DATA>;
  42. # 两种参数传递风格,可以传递对其方式,也可以默认使用左对齐
  43. print table \@a;
  44. print table data => \@a, align => 1;
  45. print table data => \@a;
复制代码

作者: yinyuemi    时间: 2013-04-01 18:46
回复 3# Perlvim


    我觉得这两种情况的确不好判断,你可以考虑像excel的输出格式,比如
  1. abc     abcd    "ief
  2. eing"
  3. din     dge     kxd
  4.                ge
复制代码
上面" ",指明了ief 和eing是在一起的,而kxd和ge不是
作者: Perlvim    时间: 2013-04-01 20:23
回复 11# yinyuemi

就假设没有这样的异常情况,因为这些异常情况都可以用单独的程序进行修正


作者: Perlvim    时间: 2013-04-01 22:16
回复 12# yinyuemi

非常感谢音乐迷的梦幻作品。

每个字段的宽度不同,默认取的是最长字段宽度,这样浪费较大。应当取当前列中最宽的长度作为当前列的宽度。

实际应用中,多个字段,通常一个字段较长,必须要分行处理。就好像 <<Perl 口袋书>>中,大量的使用表格型数据。

在不同的终端上,表格宽度需要调整,所以,这个算法要想成为实用的算法,还需要稍微改进一下。


   
作者: yinyuemi    时间: 2013-04-01 22:29
回复 14# Perlvim


    是的,应该去当前列最大的,用个hash保存,应该就能实现,有时间我再改改
作者: rubyish    时间: 2013-04-02 09:03
我的 V2:
  1. #!/usr/bin/perl
  2. sub table {
  3.     my %i = ( align => 1, title => '', space => 1, samewidth => 1 );
  4.     @_ % 2 ? do { $i{data} = shift } : do { %i = ( %i, @_ ) };
  5.     my ( $p, $t, $c, $w ) = @i{qw/align title space samewidth/};
  6.     my @d = @{ $i{data} };
  7.     my ( $l, $s, $e, @l, $L ) = ( 0, $" x $c, scalar @{ $d[0] } );
  8.     my %T = (
  9.         b => [qw(: - :)],
  10.         m => [qw(: + :)],
  11.         h => '-', v => '|'
  12.     );
  13.    
  14.     @l = $w
  15.     ? do { map $l < $_ && ( $l = $_ ), map length, map @$_, @d; ($l+$c*2) x $e }
  16.     : do { map {
  17.              my ( $l, $a ) = ( 0, $_ );
  18.              map $l < $_ && ( $l = $_ ), map length $d[$_][$a], 0 .. $#d; $l + $c * 2
  19.            } 0 .. $e - 1 };
  20.                
  21.     $L += $_ for @l;
  22.     $L += $e - 1;
  23.    
  24.     my ( $hm, $hb ) =
  25.       map { $T{$_}[0].join( $T{$_}[1], map $T{h} x $_, @l )."$T{$_}[2]\n"
  26.     } qw/m b/;
  27.     my $title = "$T{v}$s$t".$" x ( $L - $c - length $t )."$T{v}\n";
  28.     my $A = sub {
  29.       my $i;
  30.       map { my $x = $" x ( $l[$i++] - $c - length ); $p ? "$s$_$x" : "$x$_$s" } @_;
  31.     };
  32.     my $text = sub { $T{v} . join( $T{v}, $A->(@_) ) . "$T{v}\n" };
  33.     $hb.( $t ? $title.$hb : '' ).join( $hm, map $text->(@$_), @d ).$hb;
  34. }

  35. my @a = map [split], <DATA>;

  36. print table \@a;
  37. print table data => \@a, align => 1, space => 0;
  38. print table data => \@a, samewidth => 0, title => 'Africa', align => 0, space => 1;
  39. __DATA__
  40. Abid        Banjul       Conakry        Harare
  41. Accra       Bissau       Dakar          Johanne
  42. Addis       Blantyre     Dar_es_Salaam  Juba
  43. Algie       Brazzaville  Djibouti       Kampala
复制代码

作者: rubyish    时间: 2013-04-02 09:48
dahe_1984 发表于 2013-04-01 07:06
回复 6# rubyish

呵呵,您写的代码永远这么高深莫测,我总是看不懂

我也是!{:3_188:}
基本靠猜。{:3_203:}
作者: kk861123    时间: 2013-04-02 09:55
rubyish 发表于 2013-04-02 09:48
我也是!
基本靠猜。

给变量起个好名字吧,别总是阿猫阿狗的{:3_185:}
作者: rubyish    时间: 2013-04-02 10:05
kk861123 发表于 2013-04-02 05:55
给变量起个好名字吧,别总是阿猫阿狗的

{:3_188:}

  1. my $cat  = 'cat';
  2. my $dog = 'dog';
复制代码





欢迎光临 Chinaunix (http://bbs.chinaunix.net/) Powered by Discuz! X3.2