我需要通过Perl脚本创建动态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)
所以find / replace以某种方式工作,但我无法确定如何确定!
我做错了什么?
答案 0 :(得分:2)
首先,提出一些建议:
在表达式中有太多括号,加上变量和函数,我们看不到它们的定义会使您的代码难以直观地解析。您也没有发布显示问题的小型自包含脚本,这意味着任何试图帮助您的人都必须设置测试脚本。让其他人轻松。
例如,您可以按如下方式重写您定义$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
形式,那么替换会切换替换中的工作表名称,并且只会放在范围内。
这个原因似乎与Spreadsheet::WriteExcel::Formula
中的解析器设置有关。普通范围被解析为range2d
标记,而具有图表引用的范围被解析为range3d
标记。稍后,在repeat_formula
中,替换将在令牌上完成。根据我的收集,range2d
令牌看起来像'_ref2dA1:A10'
。因此,'_ref2dA1:A2'
会在重复公式中转换为'_ref2dFeb!A1:10'
。但是,当它传递给Spreadsheet::WriteExcel::Formula::parse_tokens
时,代码仍会根据前缀将其分类为range2d
令牌。我收集了最后的电话$parse_str .= $self->_convert_ref2d($token, $class);
,然后将其转回'_ref2dA1:A10
。我不确定这是否可归类为错误,但用户的POV肯定是意外的。
因此,解决方案是输入保证存在的工作表的名称。
这是一个测试脚本:
#!/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': $!";
这是截图:
答案 1 :(得分:1)
store_formula()/repeat_formula()
方法是慢速公式解析使用Parse :: RecDescent的历史解决方法。它们仅用于替换简单和类似范围。
将表达式从2D范围更改为3D范围是行不通的,因为表示每个公式的解析二进制结构非常不同,并且无法通过简单替换进行更改。
您可以在Spreadsheet::WriteExcel中解决此问题,但我建议您使用Excel::Writer::XLSX,它是Spreadsheet :: WriteExcel的API兼容替代品,并且不需要store_formula()/repeat_formula()
加快重复的公式。
最后一点,两个Excel编写器模块都带有::Utility模块用于范围生成。