2

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)

したがって、検索/置換はどういうわけか機能していますが、実際にはどのように特定することはできません!

私は何が間違っているのですか?

4

2 に答える 2

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': $!";

そしてここにスクリーンショットがあります:

正しい式を示すスクリーンショット

于 2012-05-04T19:41:43.297 に答える
1

これらのstore_formula()/repeat_formula()メソッドは、式の解析が遅い場合の歴史的な回避策であり、Parse::RecDescentを使用します。これらは、単純で類似した範囲を置き換えることのみを目的としています。

式を2D範囲から3D範囲に変更しても、これらの各式を表す解析済みのバイナリ構造は大きく異なり、単純な置換では変更できないため、機能しません。

これはSpreadsheet::WriteExcelで回避できますが、代わりに、Spreadsheet :: WriteExcelのAPI互換の代替品であり、繰り返しの数式を高速化する必要がないExcel :: Writer::XLSXを使用することをお勧めします。store_formula()/repeat_formula()

最後に、両方のExcelライターモジュールには、範囲生成用の::Utilityモジュールが付属しています。

于 2012-05-04T22:45:31.690 に答える