【问题标题】:perl class to parse specified csv file types of similar data with different columnsperl 类解析具有不同列的相似数据的指定 csv 文件类型
【发布时间】:2012-07-29 18:09:12
【问题描述】:

我正在尝试编写一个 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 列表。但是,现在的方法似乎不是我要解决的问题的优雅/简单的解决方案。

所以我想问/cmets 是:

是否有更好/更简单的方法来解决这个问题(可能通过工厂模式等设计模式)?
还是我试图解决一些看起来很简单却让事情变得非常复杂的问题?

【问题讨论】:

    标签: perl oop moose


    【解决方案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);
    }
    

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

    【讨论】:

    • 感谢您的代码示例,使用配置文件是我认为可以失败的方法之一,因此让其他人按照同样的思路思考是令人放心的
    【解决方案2】:

    这样做并不是一个坏主意。让我们保持简单!

    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;
    

    【讨论】:

    • 这与我考虑使用的一种方式相似,很高兴知道我不仅仅是让事情复杂化
    • 我确实在 Perl 中使用工厂类,但在脚本语言中,您通常可以找到不同的解决方案 - 主要是因为您没有强类型。大多数设计模式都是为类似 C++ 的语言而设计的,但是当您可以直接访问符号表时,情况就完全不同了 :)
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-12-04
    • 1970-01-01
    相关资源
    最近更新 更多