我想使用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
中的变量?我一直在努力奋斗。
真的很感谢你的帮助!
答案 0 :(得分:2)
您可以使用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或更高版本。
对不起,我使用了一些构造而没有正确解释它们。
我可以从一个开始计数器。每次点击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模块。如果要查看散列或数组,请确保将数据结构作为引用传递。 E.g:
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
函数的这种用法将按字母顺序升序对其参数进行排序。