用Perl烹饪,第二部分

编辑注:Perl烹饪手册的第二版已经发布,所以本周我们继续突出展示新版本中的食谱——供您品尝。本周的摘录包括第14章(“数据库访问”)和第18章(“互联网服务”)中的食谱。并且请务必下周再回来查看更多新的食谱,包括提取表格数据、对元素或文本进行简单更改以及使用HTML::Mason进行模板化。

示例食谱:不使用数据库服务器使用SQL

问题

您想进行复杂的SQL查询,但不想维护关系型数据库服务器。

解决方案

使用CPAN的DBD::SQLite模块

use DBI;
 
$dbh = DBI->connect("dbi:SQLite:dbname=/Users/gnat/salaries.sqlt", "", "",
                    { RaiseError => 1, AutoCommit => 1 });
 
$dbh->do("UPDATE salaries SET salary = 2 * salary WHERE name = 'Nat'");
 
$sth = $dbh->prepare("SELECT id,deductions FROM salaries WHERE name = 'Nat'");
# ...

讨论

SQLite数据库存储在一个单个文件中,通过DBI构造函数中的dbname参数指定。与大多数关系型数据库不同,这里没有数据库服务器——DBD::SQLite直接与文件交互。多个进程可以同时从同一数据库文件中读取(通过SELECT),但只有一个进程可以做出更改(而其他进程在更改进行时将被阻止读取)。

SQLite支持事务。也就是说,您可以对不同的表进行多个更改,但直到您提交它们,更新才不会写入文件。

use DBI;
$dbh = DBI->connect("dbi:SQLite:dbname=/Users/gnat/salaries.sqlt", "", "",
                    { RaiseError => 1, AutoCommit => 0 });
eval {
  $dbh->do("INSERT INTO people VALUES (29, 'Nat', 1973)");
  $dbh->do("INSERT INTO people VALUES (30, 'William', 1999)");
  $dbh->do("INSERT INTO father_of VALUES (29, 30)");
  $dbh->commit(  );
};
if ($@) {
      eval { $dbh->rollback(  ) };
      die "Couldn't roll back transaction" if $@;
}

SQLite是一个无类型数据库系统。无论您在创建表时指定了什么类型,您都可以将任何类型(字符串、数字、日期、二进制大对象)放入任何字段。实际上,您甚至可以创建一个没有指定任何类型的表。

CREATE TABLE people (id, name, birth_year);

数据类型仅在比较发生时才会发挥作用,无论是通过WHERE子句还是当数据库需要排序值时。数据库忽略列的类型,只查看被比较的具体值的类型。与Perl一样,SQLite只识别字符串和数字。两个数字作为浮点值进行比较,两个字符串作为字符串进行比较,当比较两种不同类型的值时,数字总是小于字符串。

只有一种情况下SQLite会查看您为列声明的类型。要获取自动增长的列,例如唯一的标识符,请指定类型为“INTEGER PRIMARY KEY”的字段。

CREATE TABLE people (id INTEGER PRIMARY KEY, name, birth_year);

示例14-6展示了如何实现这一点。

示例14-6: ipk

  #!/usr/bin/perl -w
  # ipk - demonstrate integer primary keys
  use DBI;
  use strict;
  my $dbh = DBI->connect("dbi:SQLite:ipk.dat", "", "",
  {RaiseError => 1, AutoCommit => 1});
  # quietly drop the table if it already existed
  eval {
    local $dbh->{PrintError} = 0;
    $dbh->do("DROP TABLE names");
  };
  # (re)create it
  $dbh->do("CREATE TABLE names (id INTEGER PRIMARY KEY, name)");
  # insert values
  foreach my $person (qw(Nat Tom Guido Larry Damian Jon)) {
    $dbh->do("INSERT INTO names VALUES (NULL, '$person')");
  }
  # remove a middle value
  $dbh->do("DELETE FROM names WHERE name='Guido'");
  # add a new value
  $dbh->do("INSERT INTO names VALUES (NULL, 'Dan')");
  # display contents of the table
  my $all = $dbh->selectall_arrayref("SELECT id,name FROM names");
  foreach my $row (@$all) {
    my ($id, $word) = @$row;
    print "$word has id $id\n";
  }

SQLite可以存储8位文本数据,但不能存储ASCII NUL字符(\0)。唯一的解决方案是在存储和检索数据之前进行自己的编码(例如URL编码或Base64)。这对于声明为BLOB的列也适用。

另请参阅

“使用DBI执行SQL命令;”CPAN模块DBD::SQLite的文档;SQLite的主页http://www.hwaci.com/sw/sqlite/

示例食谱:在邮件中发送附件

问题

您想发送包含附件的邮件;例如,您想发送PDF文档。

解决方案

使用CPAN的MIME::Lite模块。首先,创建一个代表多部分消息的MIME::Lite对象

use MIME::Lite;
 
$msg = MIME::Lite->new(From    => '[email protected]',
                       To      => '[email protected]',
                       Subject => 'My photo for the brochure',
                       Type    => 'multipart/mixed');

然后,通过attach方法添加内容

$msg->attach(Type        => 'image/jpeg',
             Path        => '/Users/gnat/Photoshopped/nat.jpg',
             Filename    => 'gnat-face.jpg');
 
$msg->attach(Type        => 'TEXT',
             Data        => 'I hope you can use this!');

最后,发送消息,可选地指定如何发送它

$msg->send(  );            # default is to use sendmail(1)
# alternatively
$msg->send('smtp', 'mailserver.example.com');

讨论

MIME::Lite模块创建并发送带有MIME编码附件的邮件。MIME代表多媒体互联网邮件扩展,是附加文件和文档的标准方式。然而,它不能从邮件消息中提取附件——为此您需要查看“从邮件中提取附件”的食谱。

在创建和添加到MIME::Lite对象时,将参数作为命名参数对的列表传递。该对传递了邮件头(例如,FromToSubject)以及特定于MIME::Lite的参数。一般来说,邮件头应带有尾随冒号。

$msg = MIME::Lite->new('X-Song-Playing:' => 'Natchez Trace');

然而,MIME::Lite接受表18-2中的标题,而不带尾随冒号。*表示通配符,因此Content-*包括Content-TypeContent-ID,但不包括Dis-Content

已批准 加密 接收 发送者
密送 来自 引用 主题
抄送 关键词 回复到
注释 消息-ID 重发-* X-*
内容-* MIME版本 退信路径  
日期 组织    

MIME::Lite选项的完整列表见表18-3

数据 FH 立即读取
日期戳 文件名 顶部
处置 标识 类型
编码 长度  
文件名 路径  

MIME::Lite选项及其值决定了附件的内容和方式。

Path 包含要附加数据的文件。

Filename 消息读取者保存文件的默认文件名。默认情况下,这是从Path选项(如果指定了Path)中获取的文件名。

Data 要附加的数据。

Type 要附加数据的Content-Type

Disposition 要么是inline要么是attachment。前者表示读取者应将数据作为消息的一部分显示,而不是作为附件。后者表示读取者应显示一个解码并保存数据的选项。这最多是一个提示。

FH 从中读取附件数据的打开文件句柄。

有几个有用的内容类型:TEXT表示text/plain,这是默认值;BINARY类似地是application/octet-stream的简称;multipart/mixed用于带有附件的消息;application/msword用于Microsoft Word文件;application/vnd.ms-excel用于Microsoft Excel文件;application/pdf用于PDF文件;image/gifimage/jpegimage/png分别用于GIF、JPEG和PNG文件;audio/mpeg用于MP3文件;video/mpeg用于MPEG电影;video/quicktime用于Quicktime(.mov)文件。

发送消息的两种方式是使用sendmail(1)或使用Net::SMTP。通过调用带有第一个参数为"smtp"send来指示Net::SMTP。剩余参数是Net::SMTP构造函数的参数。

# timeout of 30 seconds
$msg->send("smtp", "mail.example.com", Timeout => 30);

如果你计划创建多个MIME::Lite对象,请注意,以类方法的形式调用send会更改发送消息的默认方式。

MIME::Lite->send("smtp", "mail.example.com");
$msg = MIME::Lite->new(%opts);
# ...
$msg->send(  );                   # sends using SMTP

如果你打算处理多个消息,还应该查看ReadNow参数。这指定了附件的数据应立即从文件或文件句柄中读取,而不是在发送、写入或转换为字符串时读取。

发送消息不是你可以用它做的唯一事情。你可以获取最终消息作为字符串。

$text = $msg->as_string;

print方法将消息的字符串形式写入文件句柄。

$msg->print($SOME_FILEHANDLE);

示例18-3是一个程序,它将命令行上给出的文件名作为附件发送。

示例18-3: mail-attachment

#!/usr/bin/perl -w
# mail-attachment - send files as attachments
 
use MIME::Lite;
use Getopt::Std;
 
my $SMTP_SERVER = 'smtp.example.com';           # CHANGE ME
my $DEFAULT_SENDER = '[email protected]';      # CHANGE ME
my $DEFAULT_RECIPIENT = '[email protected]';# CHANGE ME
 
MIME::Lite->send('smtp', $SMTP_SERVER, Timeout=>60);
 
my (%o, $msg);
 
# process options
 
getopts('hf:t:s:', \%o);
 
$o{f} ||= $DEFAULT_SENDER;
$o{t} ||= $DEFAULT_RECIPIENT;
$o{s} ||= 'Your binary file, sir';
 
if ($o{h} or !@ARGV) {
    die "usage:\n\t$0 [-h] [-f from] [-t to] [-s subject] file ...\n";
}
 
# construct and send email
 
$msg = new MIME::Lite(
    From => $o{f},
    To   => $o{t},
    Subject => $o{s},
    Data => "Hi",
    Type => "multipart/mixed",
);
 
while (@ARGV) {
  $msg->attach('Type' => 'application/octet-stream',
               'Encoding' => 'base64',
               'Path' => shift @ARGV);
}
 
$msg->send(  );

另请参阅

MIME::Lite的文档


奥莱利公司与联合出版社最近发布了(2003年8月)Perl Cookbooks,第2版。

标签

反馈

这篇文章有问题吗?请在GitHub上打开一个issue或pull request来帮助我们:GitHub