为什么在Perl的Spreadsheet :: WriteExcel模块中使用repeat_formula时,我的2D范围不会被3D范围所取代?
我需要通过Perl脚本创build一个dynamic的Excel公式。
我存储一个包含假范围的公式
my $stored_formula = $worksheet->store_formula('=MEDIAN(A1:A2)');
在脚本中,我计算了新的范围
$formula_range = $mm_arr[$mmindx-1] . "!" . num2col(2+$form_off) . ((($serv-1)*5)+5) . ":" . num2col((2+31+$form_off)) . ((($serv-1)*5)+5);
其中@mm_arr
是一个数组("Jan", "Feb", …)
而num2col
是我用来将列号转换为字母的函数。
下面一行是repeat_formula
$worksheet->repeat_formula( (($serv-1)*5)+4, 1+$mmindx, $stored_formula, undef, 'A1:A2', $formula_range );
我期望得到:
=median(Feb!K3:AH3)
但是我得到:
=median(K3:AH3)
所以发现/replace是不知何故的工作,但我不能确定如何!
我究竟做错了什么?
首先,几点build议:
在你的expression式中有太多的括号,加上variables和函数的定义是我们看不到的,所以你的代码难以用视觉来parsing。 你也没有发布一个小的,自包含的脚本来展示这个问题,这意味着任何试图帮助你的人都必须build立一个testing脚本。 使其他人容易。
例如,您定义$formula_range
可以重写为:
my $formula_range = sprintf('%s!%s%d:%s%d', $mm_arr[$mmindx - 1], num2col(2 + $form_off), 5 + 5 * ($serv - 1), num2col(2 + 31 + $form_off), 5 + 5 * ($serv - 1), );
请记住,您可以使用Spreadsheet :: WriteExcel :: Utility提供的函数在单元格表示法和行/列索引之间进行转换。
问题似乎是,如果存储的公式不是以SheetName!Range
的formsSheetName!Range
,那么replace就会replacereplace中的工作表名称,并且仅放入范围中。
原因似乎与Spreadsheet::WriteExcel::Formula
的parsing器设置有关。 一个普通范围被parsing为一个range2d
标记,而一个带有表单引用的范围被parsing为一个range3d
标记。 稍后,在repeat_formula
,replace是在令牌上完成的。 从我所能得到的结果range2d
, range2d
标记看起来像是'_ref2dA1:A10'
。 所以, '_ref2dA1:A2'
被重复公式转换为'_ref2dFeb!A1:10'
。 但是,当它传递给Spreadsheet::WriteExcel::Formula::parse_tokens
,代码依然会根据前缀将其分类为range2d
标记。 我收集了最后一次调用$parse_str .= $self->_convert_ref2d($token, $class);
然后把它变回'_ref2dA1:A10
。 我不确定这是否可以归类为一个错误,但是用户的POV肯定是意外的。
所以,解决方法是放入保证存在的工作表的名称。
这是一个testing脚本:
#!/usr/bin/env perl use strict; use warnings; use Spreadsheet::WriteExcel; use Const::Fast; const my @MONTHS => qw(Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec); const my $WORKBOOK => 'test.xls'; my $book = Spreadsheet::WriteExcel->new($WORKBOOK) or die "Cannot open '$WORKBOOK': $!"; my %sheets = map {$_ => $book->add_worksheet($_)} Summary => @MONTHS; for my $m (@MONTHS) { for my $n (1 .. 10) { $sheets{ $m }->write_number($n - 1, 0, $n); } } my $formula = $sheets{Summary}->store_formula('=MEDIAN(Summary!A1:A2)'); for my $n (0 .. 1) { my $replacement = sprintf(q{'%s'!A1:A10}, $MONTHS[$n]); $sheets{Summary}->repeat_formula($n, 0, $formula, undef, 'Summary!A1:A2' => $replacement ); } $book->close or die "Error closing '$WORKBOOK': $!";
这里是一个截图:
store_formula()/repeat_formula()
方法是使用Parse :: RecDescent的缓慢公式分析的历史解决方法。 它们仅用于replace简单和相似的范围。
将expression式从2D范围更改为3D范围不会起作用,因为表示每个公式的parsing二进制结构是非常不同的,不能用简单的replace来更改。
你可以在Spreadsheet :: WriteExcel中解决这个问题,但是我build议你使用Excel :: Writer :: XLSX这是一个API兼容替代Spreadsheet :: WriteExcel,并且不需要store_formula()/repeat_formula()
加快重复的公式。
最后要说明的是,两个Excel写入器模块都带有:: Utility模块,用于范围生成。