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

only taking certain values from a list in perl

首先我会描述我有什么,然后是问题。

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

----------- 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',
)

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

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

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

现在针对我实际遇到的问题: 我需要一种方法来只对 header 和地图中的值进行插入。 在作为示例给出的数据文件中,有几个名称不在地图中,有没有办法可以忽略它们以及 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 库是否可以在这种情况下工作。

您通常会将有效索引集放在一个列表中,然后使用 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] );

...

因为这是一个关于四个不同问题的问题,所以我将采用更高层次的方法来解决广泛的问题,并将编程细节留给您(或者您可以提出有关细节的新问题)。

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

让 headers 成为 XML 评论更糟糕,现在你正在解析评论;评论应该被忽略。如果您必须保留混合 XML/CSV 格式,请将 headers 放入适当的 XML 标记中。否则使用 XML 有什么意义?

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

开始

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

当您最终将其归结为 Text::CSV_XS object 时,在循环中调用 getline_hr 以将行作为散列获取,应用您的映射,然后插入到您的数据库中。 @mob's solution is fine, but I would go with SQL::Abstract 生成 SQL 而不是手动生成。这将防止 SQL 注入攻击以及更普通的东西,例如包含 SQL 元字符和保留字的 headers。

将解析数据的处理与数据解析分开很重要。我很确定可怕的数据格式会发生变化,要么变得更糟,要么变得更好,而且您不想将代码绑定到它。