编者的话:最新版的 Perl Cookbook 马上就要放上书店的书架开始发售了,这里我们介绍一些第二版里没有的内容,你可以先看看这些节选的内容。这周我们要介绍的是从第14章“数据库操作”和第十八章“互联网服务”中节选的内容。一定记住下周还来这个地方哦,我们会介绍脱离数据库来使用 SQL 语言,从表格中提取数据,用 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 定义的数据库不是服务器/客户短架构,没有服务器,它直接与文件交换数据。多个进程可以同时从一个文件中读取数据(此时用 SELECTs 命令),但是只有一个进程能向文件中写数据(当一个进程写数据的时候其它的进程被挂起)。SQLite 支持事务功能,也就是,你可以向多个表做出一系列改动,但是只要你不向 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 定义的数据库里面没有数据类型这个概念。不管你在创建一个表的时候指定的是什么数据类型,以后你可以在其中放入任何类型的数值(包括字符型,数字型,日期型,二进制对象/blob)。实际上,创建表的时候你甚至可以不指定数据类型。 CREATE TABLE people (id, name, birth_year);
SQLite 只有在要比较数据的时候,如用 WHERE 子句或对某些值进行排序,才会考虑数据类型。SQLite 在比较数据的时候,只考虑被比较对象的类型,而不管被比较对象所在列的其它数据是什么类型。像 Perl 一样,SQLite 只能识别字符型和数字型。两个数值总是以浮点类型进行比较,两个字符串直接比较。当不同类型的数据比较的时候,数字总是比字符小。 只有一种情况 SQLite 才会关心你为某一列申明的数据类型(需要创建一个值自增加的列的时候)。你可以把这列的类型指定为:“INTEGER PRIMARY KEY”。 CREATE TABLE people (id INTEGER PRIMARY KEY, name, birth_year);
例子 14-6 说明这一切是怎么工作的 例 14-6 整形主键
#!/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 中的 NULL 符“\0”。唯一的变通方法就是在你存储数据之前自行编码,然后在取出数据之后再手工解码,就象 URL 编码或 Base64 编码方式一样。 这甚至可以用在 BLOB 字段里面。
参照:“Executing an SQL Command Using DBI” CPAN上 DBD::SQLite 模块的正式文档。它在 SQLite 的主页上 http://www.hwaci.com/sw/sqlite
摘要:发送邮件的时候添加附件
问题:你想要发一封包含附件的邮件,比如包含一份 PDF 格式的文档
解决:用 CPAN 上的 MIME::Lite 模块。
首先,创建包含邮件各种头信息的 MIME::Lite 对象: use MIME::Lite;
$msg = MIME::Lite->new(From => 'sender@example.com',
To => 'recipient@example.com',
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( ); # 默认的方法是用sendmail规则发送
# 指定其它的方法
$msg->send('smtp', 'mailserver.example.com');
讨论:
MIME::Lite 模块创建并发送带 MIME 指定类型附件的邮件。MIME 是 Multimedia Internet Mail Extensions 的缩写,而且也是在邮件中附带各种文件文档的标准方式。但是,这个规则并不能从邮件信息中把附件提取出来。如果你想从邮件信息中提取MIME指定类型附件,可以参考这篇文章“Extracting Attachments from Mail”
当你创建 MIME::Lite 对象,以及向创建的对象中添加内容的时候。后面的参数采用“参数名=>值”的有名对形式。有名对的参数名部分应该暗示它代表的邮件头(如,From,To,Subject)以及其它其它 MIME::Lite 所特有的东西。如果参数名是邮件头,后面应该加上冒号,如: $msg = MIME::Lite->new('X-Song-Playing:' => 'Natchez Trace');
然而,当参数名代表的邮件头在表 18-2 中时,后面可以不加冒号。下表中 * 代表通配符,例如 Content-* 可以代表 Content-Type 和 Content-ID 但是不代表 Dis-Content
表 18-2: MIME::Lite 头
| Approved |
Encrypted |
Received |
Sender |
| Bcc |
From |
References |
Subject |
| Cc |
Keywords |
Reply-To |
To |
| Comments |
Message-ID |
Resent- |
X- |
| Content-* |
MIME-Version |
Return-Path |
| Date |
Organization |
MIME::Lite 参数类型的完整列表在表 18-3 中
表 18-3: MIME::Lite 参数类型
| Data |
FH |
ReadNow |
| Datestamp |
Filename |
Top |
| Disposition |
Id |
Type |
| Encoding |
Length |
| Filename |
Path |
MIME::Lite 模块的参数类型决定附件的类型和附件的添加方法:
| Path |
指定作为附件的文件的路径 |
| Filename |
指定接受方保存附件时,附件的默认文件名。如果指定了 Path 参数,那么默认的文件名就是路径中的名字 |
| Data |
指定附件添加的日期 |
| Type |
指定待添加附件的文件编码类型 |
| Disposition |
它的值只能是 inline 和 attachment。前者指定接受方打开邮件的时候附件内容会跟在邮件正文后显示,而不单独作为一个附加物。后者指定接受方应该指定一个附件的解码方法,并且保存附件,此时会有提示 |
| FH |
指定一个读取附件的开放的文件句柄 |
这儿有几个有用的附件编码类型:TEXT 代表 text/plain,为 Type 的默认值;BINARY 是 application/octet-stream 的缩写;multipart/mixed 表明邮件有附件;application/msword 表明附件为微软的 Word 文档;application/vnd.ms-excel 表明附件为微软的 Excel 文档;application/pdf 表明附件为 PDF 文档;image/gif,image/jpeg,image/png 分别指定 GIF,JPEG,PNG 文件;audio/mpeg 指定 MP3 格式文件;video/mpeg 指定 MPEG 格式影片;video/quicktime 指定Quicktime 格式文件。
发送邮件的唯一两种方法是 sendmail 和 Net::SMTP。调用 send 方法时,若第一个参数为“smtp”,则用 Net::SMTP 发送邮件。send的其它参数都传给 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 参数。它指定附件应该立即从文件或文件句柄中读取发送,而不是在发送前转化为字符串。
发送邮件不是 MIME::Lite 能做的唯一事情。你还可以用它把最后的邮件内容变成字符串:
$text = $msg->as_string;
print 方法可以把消息的字符串形式写入一个文件句柄自定的文件中: $msg->print($SOME_FILEHANDLE);
例子 18-3 是一个发送邮件的程序,它把在命令行输入的文件名作为附件 例 18-3 :发送带附件的邮件 #!/usr/bin/perl -w
# mail-attachment - send files as attachments
use MIME::Lite;
use Getopt::Std;
my $SMTP_SERVER = 'smtp.example.com'; # 可根据自己情况改变
my $DEFAULT_SENDER = 'sender@example.com'; # 同上
my $DEFAULT_RECIPIENT = 'recipient@example.com';# 同上
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( );
(http://www.fanqiang.com)