【问题标题】:parsing an xls file using perl使用 perl 解析 xls 文件
【发布时间】:2013-03-25 20:40:19
【问题描述】:

我想使用 perl 解析 xls 文件。 xls 文件遵循的结构使得解析有点棘手;

      col1      col2
row1  School    1
row2  Dean      John
row3  No.stu.   55
row4  some irrelevant stuff 
row5  School2   2
row6  Dean      Tony 
row7  No. stu.  60 
row8  some irrelevant stuff

我想要实现的输出是:

      col1 col2 col3
row1 School Dean No.stu. 
row2 1      John  55
row3 2      Tony  60 

到目前为止我一直在学习的模块是Spreadsheet::ParseExcel。任何其他模块可能会帮助我离开这里?问候,

感谢@amon 回复,它提供了部分解决我的问题的潜在方法。但是作为一个 perl 初学者,我很难消化代码。

解析部分从ROW: 开始在我脑海中浮现,那是做什么用的?而且我真的不知道

my ($key, $val) = map {$worksheet->get_cell($row, $_)} $col_min .. $col_max;

我可以将其解释为Spreadsheet::ParseExcel 文档中给出的内容吗?

for my $row ( $row_min .. $row_max ) {

for my $col ( $col_min .. $col_max ) { my $cell = $worksheet->get_cell( $row, $col );} 另外,在进入输出部分之前,我可以看看已经解析了什么吗?说,有没有打印出已经累积到表%data中的变量?我已经挣扎了一段时间。

非常感谢您的所有帮助!

【问题讨论】:

  • 你的数据标签是分开的吗?
  • 字段SchoolDeanNo.stu.是提前知道的,还是从文件中确定?是否保证没有其他字段?标记 row<N>col<N> 是用作字段值还是只是为了强调数据的表格性质而包含在内?您希望输出为 XLS 还是纯文本?
  • 对 amon,1 是的,它们也是单元格值,我“猜想”它们是事先知道的。 2 不,还有很多其他领域。 3 标记是为了强调标签的性质。 4 最好输出为 XLS。

标签: perl parsing


【解决方案1】:

您可以使用Spreadsheet::ParseExcel 来读取您的文件。遍历所有行,并将前两个字段存储在哈希中。在每四行,您可以将数据写入输出,并清除哈希:

# Adapted from the module documentation
use strict; use warnings;
use Spreadsheet::ParseExcel;

my ($infile, $outfile) = @ARGV;

my $parser   = Spreadsheet::ParseExcel->new();
my $workbook = $parser->parse($infile);

die $parser->error unless defined $workbook;

# select the first worksheet
my ($worksheet) = $workbook->worksheets();

# get bounds:
my ( $row_min, $row_max ) = $worksheet->row_range();
my ( $col_min, $col_max ) = $worksheet->col_range();

# assert that there are at least two fields per row:
$row_max - $row_min >= 1 or die "To few cells per row";

my %data; # accumulate data here

ROW:
for my $row ($row_min .. $row_max) {
  # discard every fourth row:
  if ($row - $row_min && ($row - $row_min) % 3 == 0) {
    ...; # write to output
    %data = (); # clear cache
    next ROW;
  }
  my ($key, $val) = map {$worksheet->get_cell($row, $_)} $col_min .. $col_max;
  $data{$key} = $val;
}

要编写电子表格,您可以使用Spreadsheet::WriteExcel。这看起来像

# from the module documentation
my $out_workbook  = Spreadsheet::WriteExcel->new($outfile);
my $out_worksheet = $out_workbook->add_worksheet;
...;
# write data inside our loop:
my @cols = qw/School Dean No.stu/;
for my $i (0 .. $#cols) {
  my $val = delete $data{$cols[$i]} // die "uninitialized value for $cols[$i]";
  $out_worksheet->write($row, $i, $val);
}
# do some error handling
if (my @keys = keys %data) {
  die "Unexpected field(s) [@keys] encountered";
}

对于定义或运算符//,这需要 perl5 v10 或更高版本。


更新:

很抱歉我使用了一些结构而没有正确解释它们。

每四行丢弃一次

我可以保留一个从 1 开始的计数器。每次它到达4 时,我都会跳过这一行并重置它。但是,我已经有一个行计数器,我用它来代替。我不知道第一行是0,因为$row_min 可以是任何东西。所以我转置行号$row - $row_min 以获得实际的行数。它从零开始。

每第四行,这个实际计数可以被三整除:

0 1 2 3 4 5 6 · · ·
      *     *

所以我可以使用模运算符%。但是,0 % $n == 0 对所有 $n 都是正确的(零可以通过所有数字整除),所以我必须使用特殊情况零。我通过在执行可除性测试之前检查我们的计数不为零来做到这一点。除了零之外的所有数字都是真实的,所以我可以测试我们的数字的真实性。这导致了测试

if ($row - $row_min && ($row - $row_min) % 3 == 0) { ... }

map 表达式

map 函数采用以下任一方式:

  • map EXPRESSION, LIST
  • map { BLOCK } LIST -- 注意块和列表之间没有逗号。

这很像一个漂亮的 foreach 循环:对于列表中的每个值,$_ 在我们的表达式中被设置为那个值。然后表达式返回一个被记住的值。处理完列表中的所有项目后,map 返回表达式值的列表。

例如,下面是一个 map 表达式,它对列表中的所有数字进行平方:

my @squares = map { $_ * $_ } 1 .. 10; # 1, 4, 9 16, .. 100

我使用map 获取行内的所有单元格值:我指定所有列的列表 ($col_min .. $col_max),map 块获取该列中当前行的单元格。

所以map 返回一个单元格列表,我将其分配给“左值”列表($key, $val)。列表赋值导致$key 具有第一个单元格的值,$val 具有第二个单元格的值。

用一个普通的foreach 循环编写,它看起来像:

my @cells;
for my $col ($col_min .. $col_max) {
  push @cells, $worksheet->get_cell($row, $_);
}
my $key = shift @cells;
my $val = shift @cells;

查看您的数据结构

转储数据结构以进行调试的默认方法是使用Data::Dumper 模块。如果您想查看散列或数组,请确保将数据结构作为 reference 传递。例如:

use Data::Dumper;   # at the top of your script
warn Dumper \%data; # where ever you need the info

如果你需要更好的格式,你可以自己写:

printf "Contents of %%data for row %d:\n", $row - $row_min;
for my $key (sort keys %data) {
  printf "%10s:%s\n", $key, $data{$key}
}

sort 函数的这种用法将按字母升序对其参数进行排序。

【讨论】:

    猜你喜欢
    • 2016-07-31
    • 1970-01-01
    • 2013-03-17
    • 2017-10-28
    • 2015-12-02
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-10-14
    相关资源
    最近更新 更多