将日志文件数据导入应用程序

时间:2012-12-01 13:32:39

标签: perl data-import

我的数据文件:votes.txt由与以下内容相同的行组成:

VOTE 1168241980 Campaign:ssss_uk_01B Validity:during 
Choice:Tupele CONN:MIG00VU MSISDN:00088866655598 
GUID:A34-FDS87-FHDHDH-DHDHDHD0 Shortcode:63334

每个条目用空格分隔。我目前有一个19行的样本。

字段:

CONN:MIG00VU MSISDN:00088866655598 
GUID:A34-FDS87-FHDHDH-DHDHDHD0 Shortcode:63334

没有用于此练习。

#!/usr/bin/perl 

use warnings;
use strict;
use Switch;
use DBI();

#
# _voting.pl
#

# Connect to the database.
my $dbh = DBI->connect("DBI:mysql:database=sms_voting;host=localhost",
                    "sisisi", "*********",
                    {'RaiseError' => 1});

my $sth = $dbh->prepare("INSERT INTO voting 
(epoch, validity, choice, campaigns_id, candidates_id ) VALUES (?,?,?,?,?)");

open (LOGFILE, '/var/www/voting/votes.txt') or die("Could not open log file.");

my $errors = 0;
my $campaign_id = 0;
my $candidate_id = 0;

foreach my $line (<LOGFILE>) {  

    my ($vote, $epoch, $campaign, $validity, 
 $choice, $CONN, $MSISDN, $GUID, $Shortcode) = split(' ', $line);

# parse the field:value entries...
$campaign = substr $campaign, 8, 11, '';
$validity = substr $validity, 9, 6, ''; # during
$choice = substr $choice, 7, 10, ''; # Brown

# case statements to define correct foreign keys...
 switch ($campaign) {
        case ("ssss_uk_01B")    { $campaign_id = 1 } 
        case ("ssss_uk_01C")    { $campaign_id = 2 } 
        case ("ssss_uk_01D")    { $campaign_id = 3 } 
        case ("ssss_uk_01E")    { $campaign_id = 4 } 
        case ("ssss_uk_01F")    { $campaign_id = 5 } 
        case ("ssss_uk_01G")    { $campaign_id = 6 } 
}

switch ($choice) {
        case ("Brown")      { $candidate_id = 1 } 
        case ("Cameron")    { $candidate_id = 2 } 
        case ("Balls")      { $candidate_id = 3 } 
        case ("Green")      { $candidate_id = 4 } 
        case ("Boring")     { $candidate_id = 5 } 
        case ("Tupele")     { $candidate_id = 6 } 
}

if ($epoch && $validity && $choice && $campaign_id && $candidate_id ) {

    $sth->execute($epoch, $validity, $choice, $campaign_id, $candidate_id);
    # debug
    print "$epoch $validity $choice \n"; # 1161048980 during Green
    next;
} 

$errors++;
 }

close (LOGFILE);

# debug
print qq(errors=$errors\n);

对于foreach的每个循环,$ campaign和$ choice变量通过switch语句运行,以便定义candidate_id&amp; campaign_id号码。这些外键将映射到候选人和广告系列表格。

请参阅:http://acookson.org/wp-content/themes/cookie_23112012/img/sms_voting.png了解数据库模型。

即。

INSERT INTO voting (epoch, validity, choice, campaigns_id, candidates_id ) 
VALUES (1161048980,'during','Brown', 1, 1),
(1161048980,'during','Tupele', 3, 5), ... etc

在votes.txt中检测到的任何空值都会导致$ errors变量递增。

脚本似乎成功循环遍历votes.txt但无法初始化:$ campaign_id&amp; $ candidate_id 变量resp。完成后,将错误= 19打印到终端;我的样本数据中的总行数votes.txt;含义 此文件中的每一行都无法插入数据库。

mysql> select * from voting;
Empty set (0.00 sec)

证实了这一点。

脚本报告没有语法错误;因此它的水平更低。没有开关,它工作正常;因此,这有点缩小了。我无法看到开关的问题,所以我在这里寻求建议。

2 个答案:

答案 0 :(得分:0)

不确定您的问题是什么,但这里有关于您的Switch语句和其他代码的一些指示。

据我所知,use Switch功能/模块已在perl v5.10.1中替换为given/when功能,现已弃用。就个人而言,我总是发现这些陈述有点奇怪和不可靠。并不是无法替代的,因为perl是一种如此灵活的语言。

在您的情况下,我建议使用哈希查找:

my %camp = ("ssss_uk_01B" => 1,
            "ssss_uk_01C" => 2,
            ...);
my %cand = ("Brown"       => 1,
            "Cameron"     => 2,
            ...);

$campaign_id  = $camp{$campaign};
$candidate_id = $cand{$choice}

如果您需要提供默认值,可以使用defined-or赋值运算符:

$campaign_id //= "default value here";

您的substr作业有点不对劲。首先,你使用所有四个参数,在perldoc中描述如下:

substr EXPR,OFFSET,LENGTH,REPLACEMENT

这意味着您将''替换为空字符串。这会影响变量本身,并且会删除您想要的数据(如果单独保留)。在你的情况下也不需要长度,因为它是你所追求的字符串的结尾。最后,通过使用substr语句的返回值来保存您。

最后,当我尝试你的作业时,我的第一次作业($campaign)会出现一次性错误。输出为:ssss_uk_01,您希望ssss_uk_01

要正确使用substr功能,您应该使用:

$campaign = substr $campaign, 9;
$validity = substr $validity, 9;
$choice   = substr $choice, 7;

答案 1 :(得分:0)

我同意TLP关于在查找表中使用哈希的建议。但在实际应用程序中,应使用数据库查找填充这些查找表,以避免在其他表中的数据发生更改时需要更改应用程序。

我也倾向于不太相信日志中的数据格式。使用split可以减少了解标签+冒号大小的确切长度的需要。沿着这条线,我还建议在将数据用作查找散列中的键之前对数据进行规范化。在这个例子中,我把它全部小写了。

当您使用警告时,您也可以使用警告功能来解决错误情况。在实际应用程序中,您可以在电子邮件警报系统中点击警告,以便随时了解无人值守软件何时出现问题。

执行文件操作时,or + die习惯用法是一种很好的做法。但是如果包含$ OS_ERROR(a.k.a。$!),即使是打印,这些模具也会发生更多信息。通过这种方式,您将了解权限被拒绝,不存在或磁盘已满等区别。

这些建议涉及编写可维护和有弹性的程序的问题。但我个人认为代码的美学也有助于可维护性。有意义的命名约定和代码格式化可能会在一年或两年后打开这个程序,从而进行改进或修复错误。我更喜欢创建缺乏不必要混乱的代码,例如说明显而易见的注释或使用注释来删除代码块。

保持良好的工作。世界需要更多的软件自动化。

#!/usr/bin/perl

use strict;
use warnings;
use DBI;

#
# _voting.pl
#
my $logfile = '/var/www/voting/votes.txt';

my $errors = 0;

my %campaign_id_for = (
    'ssss_uk_01b' => 1,
    'ssss_uk_01c' => 2,
    'ssss_uk_01d' => 3,
    'ssss_uk_01e' => 4,
    'ssss_uk_01f' => 5,
    'ssss_uk_01g' => 6,
);

my %candidate_id_for = (
    'brown'   => 1,
    'cameron' => 2,
    'balls'   => 3,
    'green'   => 4,
    'boring'  => 5,
    'tupele'  => 6,
);

my $dbh = DBI->connect( 
    'DBI:mysql:database=sms_voting;host=localhost',
    'sisisi', 
    '*********', 
    { 'RaiseError' => 1 } 
);

my $sth = $dbh->prepare(q{
    INSERT INTO voting (
        epoch,
        validity,
        choice,
        campaigns_id,
        candidates_id
    ) VALUES (
        ?,
        ?,
        ?,
        ?,
        ?
    )
});

my $fh;

open $fh, '<', $logfile
    or die "open $logfile: $!";

LINE:
for my $line (<$fh>) {

    my ($vote, $epoch,  $campaign, $validity, $choice,
        $CONN, $MSISDN, $GUID,     $Shortcode
    ) = split /\s+/, $line;

    ($campaign) = reverse split /:/, $campaign;
    ($validity) = reverse split /:/, $validity;
    ($choice)   = reverse split /:/, $choice;

    my $campaign_id  = $campaign_id_for{ lc $campaign };
    my $candidate_id = $candidate_id_for{ lc $choice };

    if ( $epoch && $validity && $choice && $campaign_id && $candidate_id ) {

        $sth->execute(
            $epoch,
            $validity,
            $choice,
            $campaign_id,
            $candidate_id
        );

        print "$epoch $validity $choice\n"
            or die "print: $!";

        next LINE;
    }
    else {

        warn "failed to parse: $line\n";
        $errors++;
    }
}

close $fh
    or die "close $logfile: $!";

# debug
print "error count: $errors\n"
    or die "print: $!";