文本生成表格

清华大佬耗费三个月吐血整理的几百G的资源,免费分享!....>>>

package Tools::Text::Format::FormTidy;

use Exporter;
our @ISA = qw(Exporter);
our @EXPORT_OK = qw(form_tidy);

use 5.014;
use YAML qw(Dump);
use List::Util qw(max sum);

# 填充表格字符集
our $char_hash = { qw(
  first_head    ┌
  first_concat  ┬
  first_end     ┐
  middle_head   ├
  middle_concat ┼
  middile_end   ┤
  last_head     └
  last_concat   ┴
  last_end      ┘
  form_char     ─
  form_concat  │
  )
};

sub form_tidy {
  my ($ref_lines, $indent) = @_;

  # 设置默认缩进
  $indent = 1 if (not defined $indent);

  # 要将内容以数组的形式读入
  my @lines = @{$ref_lines};

  # 过滤掉空行
  @lines = grep { not /^$/ } @lines;

  # 获取字段宽度信息和字段数组引用的数组
  my ($row_width_hash, $col_field_list) = get_row_width_hash(@lines);

  # 字段宽度取偶数宽度
  foreach my $index (keys $row_width_hash) {
    my $width = $row_width_hash->{$index};
    $row_width_hash->{$index} = $width + 1 if ($width % 2);
  }

  # 将拆分的单词按照字段长度进行填充
  my @fill_length_fields = fill_length_fields($row_width_hash, $col_field_list);

  # 生成分割线字段表
  my @middle_fields = ();
  {
    my $char = $char_hash->{form_char};
    foreach my $index (sort keys $row_width_hash) {
      my $width = $row_width_hash->{$index};
      my $str = fill_char_length($char, $width);
      push @middle_fields, $str;
    }
  }

  # 生成中间分割线
  my $middle_str = '';
  {
    my $middle_concat = $char_hash->{middle_concat};
    my $char = $char_hash->{form_concat};
    $middle_str = join $middle_concat, @middle_fields;
    $middle_str = $char . $middle_str . $char;
  }

  # 生成头分割线
  my $head_str = '';
  {
    my $first_head   = $char_hash->{first_head};
    my $first_concat = $char_hash->{first_concat};
    my $first_end    = $char_hash->{first_end};
    $head_str = join $first_concat, @middle_fields;
    $head_str = $first_head . $head_str . $first_end;
  }

  # 生成底边分割线
  my $end_str = '';
  {
    my $last_head   = $char_hash->{last_head};
    my $last_concat = $char_hash->{last_concat};
    my $last_end    = $char_hash->{last_end};
    $end_str = join $last_concat, @middle_fields;
    $end_str = $last_head . $end_str . $last_end;
  }

  # 将分隔符插入每条记录
  my @form_str_list;
  {
    my $char = $char_hash->{form_concat};
    foreach my $record (@fill_length_fields) {
      my $str = join $char, @{$record};
      $str = $char . $str . $char;
      push @form_str_list, $str;
    }
  }

  # 将表格分割线插入数据中
  my @form_lines;
  {
    my $records_amount = scalar @form_str_list;
    my $amount = 0;
    foreach my $record (@form_str_list) {
      $amount++;
      push @form_lines, $head_str   if ($amount == 1);
      push @form_lines, $record;
      push @form_lines, $middle_str if ($amount < $records_amount);
      push @form_lines, $end_str    if ($amount == $records_amount);
    }
  }
  # 添加缩进
  @form_lines = map { ' ' x $indent . $_ } @form_lines;
  # 打印表格
  return (join(chr(10), @form_lines));
}

sub fill_length_fields {
  my ($width_table, $fields_list) = @_;
  my @fill_length_fields_list = ();
  foreach my $fields (values $fields_list) {
    my $fields_amount = scalar(@{$fields});
    my @fill_length_fields = ();
    foreach my $i (1 .. $fields_amount) {
      my $fields_width = $width_table->{$i};
      my $fields_str   = $fields->[$i-1];
      my $fill_str = fill_str_length($fields_str, $fields_width);
      push @fill_length_fields, $fill_str;
    }
    push @fill_length_fields_list, [@fill_length_fields];
  }
  return @fill_length_fields_list;
}

# 填充字符到指定长度
sub fill_char_length {
  my ($char, $length) = @_;
  my $fill_length = 0;
  if (length($char) > 1) {
    $fill_length = $length / 2;
  }
  else {
    $fill_length = $length;
  }
  my $fill_str = $char x $fill_length;
  return $fill_str;
}

# 获取一个表格的最佳列宽
sub get_row_width_hash {
  my @lines = @_;
  # 从第一样得到字段数
  my $max_field = scalar(split(/\h{2,}/, $lines[0]));
  my @fields_str = ();
  my $max_row_width = {};
  my $error_mode = 0;
  my $line_number = 0;
  foreach my $line (@lines) {
    chomp $line;
    $line =~ s/^\s+//;
    $line_number++;
    # 根据至少两个以上的空格来分开
    my @words = split /\h{2,}/, $line;
    push @fields_str, [@words];
    # get the max piece number
    my $word_count = scalar @words;
    if ($word_count != $max_field) {
      say "Not $max_field fields amount:<$line>";
      $error_mode = 1;
      next;
    }
    exit if ($error_mode == 1);
    # get the max length of join line
    foreach my $i (1 .. scalar(@words)) {
      my $word = $words[$i - 1];
      my $length = length($word);
      if (exists $max_row_width->{$i}) {
        my $length_i = $max_row_width->{$i};
        if ($length > $length_i) {
          $max_row_width->{$i} = $length;
        }
      }
      else {
        $max_row_width->{$i} = $length;
      }
    }
  }
  return ($max_row_width, [@fields_str]);
}

# 向后填充字符串
sub fill_str_length {
  my ($str, $length) = @_;
  if (length($str) > $length) {
    say "str:$str length great than length:$length";
    return $str;
  }
  my $fill_str = sprintf("%-${length}s", $str);
  return $fill_str;
}

1;