只从perl中的列表中获取某些值

时间:2015-08-05 19:47:02

标签: perl csv dictionary subset sql-insert

首先,我将描述我所拥有的,然后是问题。

我有一个像这样结构的文本文件

----------- Start of file-----
<!-->
name,name2,ignore,name4,jojobjim,name3,name6,name9,pop
-->
<csv counter="1">
1,2,3,1,6,8,2,8,2,
2,6,5,1,5,8,7,7,9,
1,4,3,1,2,8,9,3,4,
4,1,6,1,5,6,5,2,9
</csv>
-------- END OF FILE-----------

我还有一个带有地图的perl程序:

 my %column_mapping = (
"name" => 'name',
"name1" => 'name_1',
"name2" => 'name_2',
"name3" => 'name_3',
"name4" => 'name_4',
"name5" => 'name_5',
"name6" => 'name_6',
"name7" => 'name_7',
"name9" => 'name_9',
)

我的动态插入语句(假设我连接到数据库正确,标题是我的标题名称数组,例如test1,test2,ect)

my $sql = sprintf 'INSERT INTO tablename ( %s ) VALUES ( %s )',
    join( ',', map { $column_mapping{$_} } @headers ),
    join( ',', ('?') x scalar @headers ); 

my $sth = $dbh->prepare($sql);

现在我遇到的问题是: 我需要一种方法来只对标题和地图中的值进行插入。 在作为例子给出的数据文件中,有几个名称不在地图中,有没有办法可以忽略它们以及csv部分中与它们相关的数字?

基本上要制作一个子集csv,把它变成:

name,name2,name4,name3,name6,name9,
 1,2,1,8,2,8,
 2,6,1,8,7,7,
 1,4,1,8,9,3,
 4,1,1,6,5,2,

这样我的插入语句只会插入地图中的那些。数据文件总是不同的,并且顺序不同,地图中会有未知数量。

理想情况下,这是一种有效的方法,因为这个脚本将经历数千个文件,并且数百万行的csv背后有数百个列。

它只是一个文本文件,而不是csv,不确定csv库是否可以在这种情况下工作。

2 个答案:

答案 0 :(得分:4)

您通常会将一组有效索引放在列表中,然后使用array slices

@valid = grep { defined($column_mapping{ $headers[$_] }) } 0 .. $#headers;

...

my $sql = sprintf 'INSERT INTO tablename ( %s ) VALUES ( %s )',
  join( ',', map { $column_mapping{$_} } @headers[@valid] ),
  join( ',', ('?') x scalar @valid);
my $sth = $dbh->prepare($sql);

...

my @row = split /,/, <INPUT>; 
$sth->execute( @row[@valid] );

...

答案 1 :(得分:3)

因为这是一个大约四个不同的问题,我将采用更高级别的方法处理大量问题并将编程细节留给您(或者您可以询问有关详细信息的新问题)。

我会尽快更改数据格式。将CSV列混合到XML文件中是奇怪且低效的,因为我确信您已经知道了。将CSV文件用于批量数据。将XML文件用于复杂的元数据。

让标题成为XML注释会更糟糕,现在你正在解析注释;评论应该被忽略。如果必须保留混合的XML / CSV格式,请将标头放入正确的XML标记中。否则,使用XML的重点是什么?

由于您要解析大文件,因此请使用XML SAX解析器。与更传统的DOM解析器不同,它必须在执行任何操作之前解析整个文档,SAX解析器将在读取文件时对其进行处理。这样可以节省大量内存。我将SAX处理作为练习,从XML::SAX::Intro开始。

在SAX解析器中,从<csv>中提取数据并在其上使用CSV解析器。 Text::CSV_XS是个不错的选择。它非常有效,并且解决了解析您可能遇到的CSV数据的所有问题。

当你最终将它归结为Text :: CSV_XS对象时,在循环中调用getline_hr以将行作为哈希值,应用映射并插入到数据库中。 @mob's solution is fine,但我会使用SQL::Abstract生成SQL而不是手动执行。这样可以防止SQL注入攻击以及包含SQL元字符和保留字的标题等更普通的内容。

将解析数据的处理与数据解析分开是很重要的。我非常确定可怕的数据格式会发生变化,无论是更糟还是更好,你不想将代码绑定到它。