1

最初に Stack Overflow の質問Using Spreadsheet::WriteExcel を参照してください。

データ構造は次のようになります。

      col1      col2    col3   col4   col5
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
row9  School    3
row10 Dean      James
row11 No.stu.   56
row12 No. teacher 20
row13 School    4
row14 Dean      Tom
row15 No.stu.   79
row16 No. teacher 21
row17 course
row18           math    2
row19           eng     4
row20 teacher   name    age   gender   race
row21           Jane    20    female   white
row22 student   name    Lee
row23           SAT     1434
row24           gender  male

私が達成したい出力は次のとおりです。

      col1  col2  col3     col4          col5         col6          col7        col8       col9
row1 School Dean No.stu.  No. teacher  course_math  course_eng  teacher_name  teacher_age  teacher_gender    teacher_race    student_name   student_SAT   student_gender
row2 1      John  55
row3 2      Tony  60
row4 3      James 56       20
row5 4      Tome  79       21              2           4            Jane        20          female                white         Lee         1434          male

そして、gangabass のおかげで、私が与えられたコードは次のとおりです。

use strict;
use warnings;

use Spreadsheet::ParseExcel;
use FindBin qw($Bin);

my ($infile) = @ARGV;

my $parser   = Spreadsheet::ParseExcel->new();
my $workbook = $parser->parse("$Bin/Test.xls");
die $parser->error unless defined $workbook;
my ($worksheet) = $workbook->worksheets();

my %data;
my $row    = 0;
my $school = "";
while (1) {
    my $cell = $worksheet->get_cell($row, 0);
    last unless defined($cell);

    my $key = $cell->value();
    my $value = $worksheet->get_cell($row++, 1)->value();

    if ($key eq "School") {

        $school = $value;
        next;
    }

    $data{$school}->{$key} = $value;
}
sleep 1;

row17-row19 の解析から始めます。最初に遭遇した問題は、(row17, col3) の空のセルでした。コードがここに到達するとエラーが発生します。Excel セルが「空」と「空白」を区別することを知っているので、元の XLS ファイルのセル形式を「一般」以外に設定することで、それをいじることができます。ただし、これは一時的な解決策にすぎません。空のセルを取得するために使用できるコマンドがあるかどうか疑問に思います。私はすでにunformatted()追加してみました:

my $unformattedvalue = $worksheet->get_cell( $row++, 1 )->unformatted();

しかし、うまくいきません。

次に、次のコードを使用して「コース」状態でデータ構造を指定しようとしました。

my %data;
my $row    = 0;
my $school = "";
my $course = ""; #Initial value for the state of course
while (1) {
    my $cell = $worksheet->get_cell($row, 0);
    last unless defined($cell);

    my $key = $cell->value();
    my $value  = $worksheet->get_cell( $row++, 1 )->value();
    my $value1 = $worksheet->get_cell( $row++, 2 )->value(); #Fetching the value in column 3

    if ($key eq "School") {

        $school = $value;
        next;
    }

    if ($key eq "course") { #Just mimicking the how we construct the structure of 'School'

         $course = $value1;
         next;
    }

    $data{$school}->{$key} = $value;  #Must be something wrong here, but can not figure out
}

コードが通過せず、Can't call method "value" on an undefined value at xxx line of 'my $value1 = $worksheet->get_cell( $row++, 2 )->value()';

要するに、私の質問は次のとおりです。

  1. 一般に、プロセスに介入せずに XLS で空のセルを取得するにはどうすればよいですか?
  2. Spreadsheet::ParseExcel を使用して次のような構造を解析するにはどうすればよいですか

    row17 course
    row18           math    2
    row19           eng     4
    
4

2 に答える 2

2

あなたはしては$row++いけないことをしている。

代わりに、次のコードを開始点として試してください。

my %data;
my $state = "";
my $school = "";
my $student = "";
my ( $row_min, $row_max ) = $worksheet->row_range();
my $row = $row_min;
while ($row <= $row_max) {
    my $cell0 = $worksheet->get_cell( $row, 0 );
    my $cell1 = $worksheet->get_cell( $row, 1 );

    if (defined($cell0)) {
        my $key = $cell0->value();
        if ($key eq 'School') {
            $state = 'school';
            $school = $cell1->value();
        } elsif ($key eq 'course') {
            $state = 'course';
        } elsif ($key eq 'teacher') {
            $state = 'teacher';
        } elsif ($key eq 'student') {
            $state = 'student');
            $student = $worksheet->get_cell( $row, 2 )->value();
        } else {
            $data{$school}{$key} = $cell1->value();
        }
    } elsif ($state eq 'course') {
        # process columns for course
    } elsif ($state eq 'teacher') {
        # process columns for teacher
    } elsif ($state eq 'student') {
        # process columns for student
    }
    $row++;
}
use Data::Dumper;
print Dumper(\%data);

アップデート:

教師の行を処理するには、最初の while ループの外で %teacher_columns ハッシュを宣言し、さらに col_range() メソッドを使用して $col_min と $col_max を宣言します。次に、$key eq 'teacher'elsif 句内で、次のようにします。

%teacher_columns = (); # clear it out in case column names are different for this school
for my $col (2 .. $col_max) {
    my $cell = $worksheet->get_cell( $row, $col );
    $teacher_columns{$cell->value()} = $col if defined($cell) and $cell->value();
}

次に、state eq 'teacher'elsif 節で、次のようにします。

foreach my $key (keys %teacher_columns) {
    my $cell = $worksheet->get_cell($row, $teacher_columns{$key});
    if (defined($cell)) {
      # store cell data into proper location of your data hash
    }
}
于 2013-04-09T16:49:09.267 に答える
0

これは私の宿題です:

列のプロセスを追加しただけです。

elsif ($state eq 'student') {
        my $key = $cell1->value();
        $data{$school}{$student}{$key} =$cell2->value();
}  elsif ($state eq 'course') {
       my $key = $cell1->value();
        $data{$school}{$course}{$key} =$cell2->value();

は次のように$cell2定義されます。

my $cell2 = $worksheet->get_cell( $row, 2 );

それは私に与えます:

$VAR1 = {
          '4' => {
                   'course' => {
                                 'math' => '2',
                                 'eng' => '4'
                               },
                   'No.Stu' => '79',
                   'No.Teacher' => '21',
                   'Lee' => {
                              'SAT' => '1434',
                              'gender' => 'male'
                            },
                   'Dean' => 'Tom'
                 },
          '1' => {
                   'No.Stu' => '55',
                   'Dean' => 'John'
                 },
          '3' => {
                   'No.Stu' => '56',
                   'No.Teacher' => '20',
                   'Dean' => 'James'
                 },
          '2' => {
                   'No.Stu' => '60',
                   'Dean' => 'Tony'
                 }
        };

これはよさそうだ。

しかし、構造がいくつか異なるため、教師の列を処理する方法についてはわかりません。

        col1      col2    col3   col4   col5
  row20 teacher   name    age   gender   race
  rwo21           Jane    20    female   white

列をループしてセル値をキーとして取得しようとしましたが、成功しませんでした:

my ( $col_min, $col_max ) = $worksheet->col_range();
my $col = $col_min;
my $cell3 = $worksheet->get_cell(++$row, $col );

elsif ($state eq 'teacher') {
        while ($col <= $col_max) {
            my $key = $worksheet->get_cell($row, $col++ );
        }
           $data{$school}{$teacher}{$key} =$cell3->value();
    }

助言がありますか?

更新: @imran の提案に従いましたが、かなりうまくいきました。次のコードを使用して、解析したものを新しい XLS ファイルに書き直しました。

use Spreadsheet::WriteExcel;

$workbook = Spreadsheet::WriteExcel->new('Result.xls');
$worksheet = $workbook->add_worksheet();

$col = 0;
$row = 0;

$worksheet->write( $row++, $col,
    ["School", "No.Stu", "No.Teacher", "Dean","Course_math", "Course_eng", "student_SAT", "student_name", "student_gender", "teacher_race", "teacher_name", "teacher_age", "teacher_gender"] );

foreach my $school( sort keys %data ) {

     $worksheet->write( $row++, $col,
        [ $school, @{$data{$school}}{ "No.Stu", "No.Teacher", "Dean"}, @{$data{$school}{course}}{ "math", "eng"}, @{$data{$school}{student}}{ "SAT", "name", "gender"}, @{$data{$school}{teacher}}{ "race", "name", "age", "gender"} ]) ;
}

$workbook->close();

1つの小さなことを除いて、すべてうまくいきました。のセルteacher_ageは空ですが、既に解析されていることがハッシュ テーブルに示されています。私はそれを理解することはできません...

ハッシュされた教師の部分は次のとおりです。

           'teacher' => {
                          'race' => 'white',
                          'name' => 'Jane',
                          'age ' => '25',
                          'gender' => ' female'
                        },

そして、私が使用したコードはまさにあなたが提案したものですが、再フォーマットされた xls の「teacher_age」のセルです。ファイルが空です。

更新 2:「状態」に関する質問に戻りますが、常に次の行から解析を開始しますか? 次のデータ構造がある場合はどうなりますか。

 School    1
 course    math
           eng
 ...
 School     2
 course    phy
 ...
 School     3
 course    chem
           gym
           music

eng古いコードを使用してコース状態を定義すると、 from school 1gym and musiccourse fromしか解析できませんschool 2。さて、一列先のコースステートを決めようと思ったのですが、うまくいきませんでした。助言がありますか?

アップデート:

@imran の提案に従い、コース コードを次のように変更しました。

} elsif ($key eq 'course') {
            $state = 'course';
            $course = $worksheet->get_cell( $row, 1 )->value();
}

それに対応して、ハッシュテーブルは次のようになります。

} elsif ($state eq 'course') {
            my $key = $cell1->value();
            $data{$school}{$course}{$key} =$cell1->value();
}

ただし、解析できるのは

 '1' => {
                   'math' => {
                               'eng' => 'eng'
                             },
       }
 '3' => {
   'chem' => {
                               'gym' => 'gym',
                               'music' => 'music
                             },
      }

の解析はありませんschool2

この問題はcourse、キーに到達した直後に単純に進むことで解決されました。course keysつまり、コース テーブルの定義と処理を別々のブロックで行う代わりに。私は今持っています:

  } elsif ($key eq 'course') {
            $state = 'course';
            $course = $state;
            my $key = $cell1->value();
            $data{$school}{$course}{$key} = $key;
}

そして、それはそのトリックを行います。しかし、私はそれがどのように機能するかについてまだ興味があります。

于 2013-04-10T10:44:12.573 に答える