perl类用于解析具有不同列的类似数据的指定csv文件类型

时间:2012-07-29 18:09:12

标签: perl oop moose

我正在尝试编写一个Moose类,它使用标头解析略有不同格式的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;

MyData类只是一个简单的数据结构,其中一些属性具有默认属性(因此从上面进行了不同的初始化)。一些csv文件类型还具有需要一些操作的列(例如,需要进入简单公式的数字),这些操作是文件类型相关的。然后将此MyData返回到我的主脚本以插入到oracle中的表中。

我的目标是让MyParser处理某些指定类型的csv文件,这些文件可以根据需要进行扩展,并从get_data方法返回MyData列表。但是,现在的方法似乎不是我想要解决的优雅/简单的解决方案。

所以我想问/评论是:

是否有更好/更简单的解决方法(可能通过工厂模式等设计模式)? 或者我是在尝试解决一些看起来很简单并让事情变得错综复杂的事情?

2 个答案:

答案 0 :(得分: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',
    },
};

然后您可以使用以下内容替换 if-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);
}

将映射规则分开可以很容易地添加对新文件类型的支持或在一个地方更改现有文件类型。

答案 1 :(得分: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;