2012-07-29 17 views
1

我想寫解析略有不同格式的CSV文件與頭一個木類和返回代表對文件中的數據對象的列表。下面的代碼的簡化版本:perl的類來解析指定CSV文件類型的類似數據的不同列

package MyParser; 

use Moose; 
use namespace::autoclean; 
use Text::CSV_XS; 

use MyData; #class that represents data for each row of csv 

has 'type' => (is => 'ro', isa => 'Str', required => 1); 

sub get_data { 
    my($self, $file) = @_; 

    open my $fh, '<', $file || die "Can't open file $!"; 

    my $csv = Text::CSV_XS->new; 
    $csv->column_names($csv->getline($fh)); 

    my @data; 
    if ($self->type eq 'filetype1'){ 
     while (my $row = $csv->getline_hr($fh)){ 
      push @data, MyData->new(field1 => $row->{col1}, 
            field2 => $row->{col2}, 
            field3 => $row->{col3}, 
            ); 
     } 
    } 
    elsif ($self->type eq 'filetype2'){ 
     while (my $row = $csv->getline_hr($fh)){ 
      push @data, MyData->new(field1 => $row->{colA}, 
            field3 => _someFunction($row->{colB}), # _someFunction does some manipulation with the data 
            field5 => $row->{colC}, 
            ); 
     } 
    } 
    elsif ($self->type eq 'filetype3'){ 
     while (my $row = $csv->getline_hr($fh)){ 
      push @data, MyData->new(field1 => $row->{column_1}, 
            field2 => _someOtherFunction($row->{column_2}), # _someOtherFunction does some manipulation with the data 
            field3 => $row->{column_3}, 
            field4 => $row->{column_4}, 
            field5 => $row->{column_5}, 
            ); 
     } 
    } 
    close $fh; 

    return \@data; 
} 

__PACKAGE__->meta->make_immutable; 

1; 

類邁德特只是一個簡單的數據結構,其中一些屬性具有默認屬性(因此從上面的不同的初始化)。某些csv文件類型也有一些需要某些操作的列(例如需要進入簡單公式的數字),這些操作是依賴於文件類型的。這個MyData然後返回到我的主腳本插入到oracle中的表中。

我的目標是MyParser處理某些指定類型的,如果需要,並從GET_DATA方法返回邁德特的列表,可擴展的CSV文件。但是,現在的方法似乎並不像我想要解決的問題那麼簡單。

所以我想問問是什麼/上意見是:

是否有解決這個(通過設計模式也許如工廠模式)的更好/更簡單的方法?
或者我想解決一些看起來很簡單的事情,讓事情變得複雜嗎?

回答

1

,而不是在IF-ELSIF-ELSIF重複代碼構造這將是清潔的,如果 你把字段映射規則到一個配置文件。例如,像這樣的數據結構:

{ 
    filetype1 => { 
     field1 => 'col1', 
     field2 => 'col2', 
     field3 => 'col3', 
    }, 
    filetype2 => { 
     field1 => 'colA', 
     field3 => { 
      function => sub {}, 
      params => ['colB'], 
     }, 
     field5 => 'colC', 
    }, 
    filetype3 => { 
     field1 => 'column1', 
     field2 => { 
      function => sub {}, 
      params => ['column_2'], 
     }, 
     field3 => 'column_3', 
     field4 => 'column_4', 
     field5 => 'column_5', 
    }, 
}; 

然後可以取代如果-ELSIF-ELSIF構建具有類似於下面 (假設映射規則已經被加載並存儲在$filetype_mappings):

while (my $row = $csv->getline_hr($fh)) { 
    my %my_data = map { 
     my $m = $filetype_mappings->{$_}; 
     $_ => (ref $m ? &{$m->{function}}(map {$row->{$_}} @{$m->{params}}) 
         : $row->{$m} 
     ); 
    } keys %$filetype_mappings; 
    push @data, MyData->new(%my_data); 
} 

將映射規則分開後,可以輕鬆地添加對新文件類型的支持或在一個位置對現有文件進行更改。

+0

感謝代碼示例,使用配置文件是我一直在想我可以下去,所以讓別人按照同樣的思路思考是令人放心的 – 1stdayonthejob 2012-07-30 21:52:09

0

這樣做並不是一個壞主意。讓我們保持簡單!

OTOH,你可以創建一個MyData的基類,其中有一個「抽象的」方法「parseData」從構造函數調用。你可以說MyData,MyData等等,都實現了他們的parseData方法。然後在GET_DATA你會簡單地做:

my($self, $file) = @_; 

open my $fh, '<', $file || die "Can't open file $!"; 

my $csv = Text::CSV_XS->new; 
$csv->column_names($csv->getline($fh)); 

my @data; 
while (my $row = $csv->getline_hr($fh)){ 
    my $class = 'MyData'.$self->type; 
    push (@data, $class->new($row)); 
} 
close $fh; 
return \@data; 
+0

這是類似於我想過使用爲好方式之一,好知道我不只是複雜的事情 – 1stdayonthejob 2012-07-30 21:54:28

+0

我使用在Perl工廠類,但在腳本語言中,你經常可以找到不同的解決方案 - 主要是因爲你沒有很強的打字。大多數設計模式都是針對C++類語言編寫的,但是當您有像直接訪問符號表這樣的東西時,這是一個完全不同的故事:) – 2012-07-31 00:48:04