论坛: 编程破解 标题: perl常用函数(适合初学者)! 复制本贴地址    
作者: DaemonTools [a7611679_]    论坛用户   登录
指令:print
语法:print Filehandle LIST
说明:这个Filehandle可以看作在I(INPUT)/O(OUTPUT)之间的一个桥梁,可以利用FILEHANDLE来做出数据读入写 出的动作.STDIN是代表从哪连输入数据,例如从电脑的键盘输入;STDOUT是代表从哪连输出数据;例如从电脑的屏 幕输出;STDERR是代表从哪连输出错误的数据,例如从电脑的屏幕输出.而在PERL语言中有三个标准FILEHANDLE: 1.STDIN(标准输入):是代表STDIN的FILEHANDLE
2.STDOUT(标准输出):是代表STDOUT的FILEHANDLE
3.STDERR(标准错误输出):是代表STDERR的FILEHANDLE如果要使用其他FILEHANDLE的时候,就要用OPEN这个函数来打开一个FILEHANDLE,我们可以用PRINT这个函数LIST的数据输出给FILEHANDLE.
在为大家介绍PRINT这个函数之前,先让我们来看看PRINT函数中特殊打印字符:

指令:#
说明:注释符号Remark宣告
示例:#这是一个注释说明


--------------------------------------------------------------------------------

指令:print
语法:print Filehandle LIST
说明:这个Filehandle可以看作在I(INPUT)/O(OUTPUT)之间的一个桥梁,可以利用FILEHANDLE来做出数据读入写 出的动作.STDIN是代表从哪连输入数据,例如从电脑的键盘输入;STDOUT是代表从哪连输出数据;例如从电脑的屏 幕输出;STDERR是代表从哪连输出错误的数据,例如从电脑的屏幕输出.而在PERL语言中有三个标准FILEHANDLE: 1.STDIN(标准输入):是代表STDIN的FILEHANDLE
2.STDOUT(标准输出):是代表STDOUT的FILEHANDLE
3.STDERR(标准错误输出):是代表STDERR的FILEHANDLE如果要使用其他FILEHANDLE的时候,就要用OPEN这个函数来打开一个FILEHANDLE,我们可以用PRINT这个函数LIST的数据输出给FILEHANDLE.
在为大家介绍PRINT这个函数之前,先让我们来看看PRINT函数中特殊打印字符:

符号 其作用
\n 换行 new line
\r 光标换行 return
\t tab键
\f 换页 form feed
\b 退回一格
\v 垂直tab键
\a 响铃Bell
\e escape键
\007 十进制ASC II码
\xff 十六进制码
\c[ 控制字符
示例: print STDOUT "网上学园\n"; 将"网上学园"加上换行显示在屏幕上.

语法: print LIST
说明: 如果省略Filehandle的话,就会把Filehandle内定为STDOUT.也就是会将LIST的数据内容显示在屏幕上.
示例: $url="www.netease.net/~zmd";
print "网上学园$url\n";
在屏幕上将会出现"网上学园www.netease.net/~zmd",如果想要让双引号内的变量失效,可以在变量的前面加上"\"符号.例如: print"网上学园\$url"; 这样它就显示:"网上学园$url"

语法: print
说明: 同省略Filehandle和LIST的话,就会以STDOUT为Filehandle,并会输出$_这个内定输出变量的数据内容. 如果$_变量是一个空字符串的话,就会显示出一个空字符串.
示例: $_="网上学园\n"; print; 就会将"网上学园"加上换行显示在屏幕上


--------------------------------------------------------------------------------

指令: printf
语法: printf Filehandle LIST
说明: 在perl语言中也提代C语言中printf的语法,用法和C语言中的用法一模一样.如果把Filehandle省略的话 ,也一样会把STDOUT当成是内定的Filehandle.在为大家介绍printf函数之前,先让我们来看看printf函数中变换符号的字符.

符号 其作用
%c 字符
%s 字符串
%d 整数
%f 浮整数
%h 十六进制码
%o 八进制码
示例:printf("chomod%d%s\n","711""cgi"); 会将chmod 711 cgi加上换行显示于屏幕上。


--------------------------------------------------------------------------------

指令:chop 语法:chop($url)
说明:把最后一个字符删除。
示例:$url="www.nease.net/~zmd/";
chop($url); 这时$url="www.nease.net/~zmd" 而这两行也可以写成chop($url="www.nease.net/~zmd/");


--------------------------------------------------------------------------------

指令:split
语法:split(/pattern/,$text,limit) 其中/pattern/是文字处理的模式,而limit是代表要分割的个数,一般可以省略。
说明:用一个指定的文字处理模式来分割$text字符串。
示例:
$text="Michael,Gevin,Mike"; @name=split(/,/,$text); #这时@name=("Michael","Gevin","Mike");
($a,$b,$c)=split(/,/,$text); #这时$a="Michael";$b="Gevin";$c="Mike";
@name=split(/,/,$string,2); #这时@name=("Michael","Gevin");
在传送CGI应用程序数据的时候会先将数据编码,其中会将FORM中第个数据字段 的数据内容用&这个符号隔开,所以在解码的时候就要以&这个符号为分割的字符,将每个数据字段分割出。例如: $text="Mike=A&Michael=B";
@name=split(/&/,$text); #这时@name=("Mike=A","Michael=B"); 而数据字段的名称和这个数据字段的值是用=这个符号来隔开,如果想取得数据 字段的名称和所对应的值的话,就用要=这个符号来分割数据字段,例如: $name=""Mike=Michael"";
($name1,$name2)=split(/=/,$list); #这时$name1="Mike";$name2="Michael";


--------------------------------------------------------------------------------

指令:keys
语法:keys(%array)
说明:取出关联数组%ARRAY中全部的key。
示例:%NAME=(1,"mike",2,"michael"); @readkey=keys(%NAMES); #这时@readkey=(1,2);


--------------------------------------------------------------------------------

指令:values
语法:values(%array)
说明:取出关联数组%ARRAY中全部的value。
示例:%NAMES=(1,"mike",2,"michael"); @readval=values(%NAMES); #这时@readval=("mike","michael");


--------------------------------------------------------------------------------

指令:reverse
语法:reverse(@array)
说明:将数组@array中的元素由后到前重新排列。
示例:@back=("A","B","C","D","E"); @back=reverse(@back); #这时@back=("E","D","C","B","A");


--------------------------------------------------------------------------------

指令:sort
语法:sort(@array)
说明:将数组中的元素由小到大排序,如果要由大到小排序的话,要加上reverse这个函数。
示例:
@abc=("d","b","c","a"); @abc=sort(@abc); #这时@abc=("a","b","c","d");
@abc=(reverse sort@abc); #这时@abc=("d","c","b","a"); 这个语法也可以写成@abc=(reverse sort(@abc));
@number=(5,2,10); @number=sort(@number); 上面示例用sort函数来排序数值的时,会出差错,因此要用下面到下面这一句。 @number=(sort{$a<=>$b}@number); #这时@number=(2,5,10);


--------------------------------------------------------------------------------

指令:length
语法:length($string)
说明:求出字符串$string的字节(bytes)值。
示例:$string="Perl5"; $size=length($string); #这时$size=5;


--------------------------------------------------------------------------------

指令:substr
语法:substr($string,offset,length) offset代表起始字符的位置,length代表引用的字符串长度,如果省略length则代表从起始值到字符串的最后一个字符长度。而offset如果是负值的话,就会从字符串右边开始指定字符。
示例:
$s=substr("perl5",2,2); #这时$s="rl";
$s=substr("perl5",2); #这时$s="rl5";
$s=substr("perl5",-2,2); #这时$s="er";


--------------------------------------------------------------------------------

指令:index
语法:index($string,$substring,position) $substring是要寻找的字符;position代表从哪一个位置开始寻找,假如省略position就从头开始找起。
说明:返回所要找寻的字符在一字符串$string中的位置,如果在字符串中找不到字符的话,则会返回-1这个 值。
示例:
$s=index("perl5","p"); #这时$s=0
$s=index("perl5","l",2); #这时$s=3
$s=index("perl5","perl"); #这时$s=-1


--------------------------------------------------------------------------------

指令:push
语法:push(@array,$string)
说明:在数组@array的最后附加新的元素 ($string)到数组@array中。
示例:@array=("one","two"); push(@array,"three"); #这时$@array=("one","two","three")


--------------------------------------------------------------------------------

指令:pop
语法:pop(@array)
说明:将数组(@array)的最后一个元素删除,并将删除的元素返回。
示例:@array=("one","two"); $rm=pop(@array); #这时@array=("one");而$rm="two";


--------------------------------------------------------------------------------

指令:unshift
语法:unshift(@array,$string) 说明:在数组@array的第一个元素前附加新的元素$string到数组@array中。 示例: @array=("one","two"); unshift(@array,"three"); #这时@array=("three","one","two")


--------------------------------------------------------------------------------

指令:shift
语法:shift(@array)
说明:将数组@array的第一个元素删除,并将删除的元素返回。
示例:@array=("one","two"); @rm=shift(@array); #这时@array=("two");而$rm="one";


--------------------------------------------------------------------------------

指令:join
语法:join($string,@array)
说明:在一数组@array的元素之间加上一指定的字符$string,并将结果返回。
示例:
@array=("one","two","three");
$total=join(":",@array); 这时$total="one:two:three";


--------------------------------------------------------------------------------

指令:grep
语法:grep(/pattern/,@array)
说明:将合文字处理模式(regular expression)的数组元素找出来。
示例:
@array=("one","on","in");
$count=grep(/on/,@array); #这时$count=2
@result=grep(/on/,@array);#这时@result=("one","on");


--------------------------------------------------------------------------------

指令:hex
语法:hex($string)
说明:将十六进制的数值转成十进制。
示例: $decimal=hex("ff"); 这时$decimal=255;


--------------------------------------------------------------------------------

指令:rand
语法:rand($interger)
说明:常和函数srand搭配来取得一随机数,如果没有先宣告stand函数的话,则取出的常数值是一个固定值。这个语法会返回一个介于0和$interger之间的数值,如果$interger省略的话,则会返回一个介于0和1 的数值。
示例:
srand; #要先宣告srand函数,才能产生随机数的效果
$int=rand(10); #$int的值会大于0而且小于10如果希望产生的乱数是整数的话,就要再加上int #这个函数
$int=int(rand(10)); #$int的值是一个整数,且值在0和9之间


--------------------------------------------------------------------------------

指令:localtime
语法:localtime(time)
说明:可返回九个有关时间的元素,在写CGI应用程序的时候常会用到系统的时间,所以在此会详细介绍这个函 数的用法。
示例:
($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst)=localtime(time);
其中: $sec代表秒数[0,59] $min代表分数[0,59] $hour代表小时数[0,23] $mday代表是在这个月的第几天[1,31] $mon代表月数[0,11],要将$mon加1之后,才能符合实际情况。$year从1990年算起的年数 $wday从星期六算起,代表是在这周中的第几天[0-6] $yday从一月一日算起,代表是在这年中的第几天[0,365] $isdst只是一个flag 知道这些变量之后,就可以在CGI应用程序中拿来应用了。此外,也可以用下面这一行指令在UNIX系统下取得系统的时间。为了避免错误发生,最好用绝对路径的方法来取得系统时间,如果绝对路径不清楚的话可以用"which data"这个指令来得知。最后要提字符的话,就不能正确执行系统的程序了。 $data='/usr/bin/data'; 而在perl5版本中,也可以用下面这一行指令来取得系统时间。 $data=localtime(time);


--------------------------------------------------------------------------------

指令:die
语法:die LIST
说明:会把LIST字符串显示出来,并退出程序。常常和$!这个代表错误信息变量一起使用。
示例:open(FILE,"$filename")||die "不能打开文件$!\n; 如果打开文件失败的话,就会显示出错误的信息,之后再退出程序。


地主 发表时间: 05-07-29 19:20

回复: 286 [unique]   版主   登录
用 Sysopen()进行更多的控制
--------------------------------------------------------------------------------
为了更好的控制文件的打开方式,可以使用 sysopen() 函数:

use Fcntl;

sysopen(FH, $filename, O_RDWR|O_CREAT, 0666)

or die "Can't open $filename for reading/writing/creating : $!";


函数 sysopen() 带有四个参数,第一个是同open()函数类似的文件句柄参数,第二个参数是不带模式信息的文件名,第三个参数是模式参数,由Fcntl 模块提供的逻辑OR运算组合起来的常数构成,第四个参数(可选),为八进制属性值(0666表示数据文件, 0777表示程序)。如果文件可以被打开,sysopen() 返回true,如果打开失败,则返回false。

不同于open()函数,sysopen()不提供模式说明的简写方式,而是把一些常数组合起来,而且,每个模式常数有唯一的含义,只有通过逻辑OR运算才能将它们组合起来,你可以设置多个行为的组合。

O_RDONLYRead-only

O_WRONLY Write-only

O_RDWR Reading and writing

O_APPEND Writes go to the end of the file

O_TRUNC Truncate the file if it existed

O_CREAT Create the file if it didn't exist

O_EXCLError if the file already existed (used with O_CREAT)


当你需要小心行事的时候,就使用sysopen() 函数,例如,如果你打算添加内容到文件中,如果文件不存在,不创建新文件,你可以这样写:

sysopen(LOG, "/var/log/myprog.log", O_APPEND, 0666)

or die "Can't open /var/log/myprog.log for appending: $!";

读入单个记录
有一个容易的方法读入filehandles:用 操作符。在标量内容下,它返回文件中的下一个记录,或者返回未定义出错信息。我们可以使用它来把一行读入到一个变量中:

$line = ;

die "Unexpected end-of-file" unless defined $line;

在循环语句中,我们可以这样写:

while (defined ($record = )) { # long-winded

# $record is set to each record in the file, one at a time

}


因为要大量进行这样的工作,通常再进行一下简化,

把记录放到$_ 中,而不是$record中:

while () {

# $_ 每次为文件中的一个记录

}

在Perl 5.004_04中,我们可以这样做:

while ($record = ) {

# $record 每次为文件中的一个记录

}


defined() 将自动加上,在Perl 5.004_04以前的版本中,该命令给出一个警示。要了解所用的Perl版本,可在命令行下打入:

perl -v

一旦我们读出了一个记录,通常打算去掉记录分隔符,(缺省值为换行符字符):

chomp($record);

Perl 4.0版本仅有chop()操作,去掉串的最后一个字符, 不管该字符是什么。chomp() 没有这么大的破坏性,如果有行分隔符存在,它仅去掉行分隔符。如果你打算去掉行分隔符,就用chomp() 来代替chop()。


B1层 发表时间: 05-08-01 13:25

回复: 286 [unique]   版主   登录
读入多个记录
如果你调用,返回文件中剩余的记录。如果你处于文件尾,则返回空表:

@records = ;

if (@records) {

print "There were ", scalar(@records), " records read.n";

}


在下面的一步中,进行赋值和测试两项工作:

if (@records = ) {

print "There were ", scalar(@records), " records read.n";

}

chomp() 也可适用对数组操作:

@records = ;

chomp(@records);

对于任何表达式,都可以进行chomp操作,故你可以在下面的一步中这样写:

chomp(@records = );


什么是记录?

记录的缺省定义为:“行”。

记录的定义由$/ 变量控制的,该变量存放所输入的记录的分隔符,因为换行符字符(根据定义!)是用来分隔行的,故其缺省值为串“n”。

例如,你可以用任何你想要替换的符号来代替“n”。

$/ = ";";

$record = ; # 读入下一个用分号分隔的记录

$/可以取其它两个有趣的值:空串("") 和undef。

读入段落
$/ =""的写法是用来指示Perl读入段落的,段落是由两个或两个以上的换行符构成的文本块。这不同于设置为"nn",后者仅读入由两行组成的文本块。在这种情况下,将出现这样一个问题:如果有连续的空行存在,例如“textnnnn”,你既可以把它解释为一个段落 ("text"),也可以解释为两个段落 ("text", 后面跟两个换行符,以及一个空段落,后面跟两个空行。)

在读入文本时,第二个解释用途不大。如果你正在读的段落出现上述情况,你不必过滤出“空”段落。

$/ = "nn";

while () {

chomp;

next unless length; # 跳过空段

# ...

}


你可以把 $/设置为undef,它用于读入后面跟着两个或多个换行符组成的段落: undef $/;

while () {

chomp;

# ...

}


读入整个文件

$/ 的其它有趣的值为undef。如果设置为该值,就将告诉Perl,读命令将把文件的剩余部分作为一个串返回:

undef $/;

$file = ;


因为改变了 $/的值,将会影响以后的每次读操作,而不仅是下一个读操作。通常,你需要将该操作限制在局部。通过下面的例子,可以把文件句柄的内容读入到一个串中:

{

local $/ = undef;

$file = ;

}


记住:Perl变量可读入很长的串。尽管你的文件大小不可以超出你的虚拟内存容量的限度,你仍可以读入尽可能多的数据。
用正则表达式对文件进行操作
一旦你有个包含了整个串的变量,你可以使用正则表达式,对整个文件进行操作,而不是对文件中的某个块进行操作。有两个有用的正则表达式标记/s和/m。一般,Perl的正则表达式对行进行处理,你可以这样写:

undef $/;

$line = ;

if ($line =~ /(b.*grass)$/) {

print "found $1n";

}


如果把我们的文件填入如下内容:
browngrass

bluegrass

则输出为:

found bluegrass

它没有找到“browngrass”,这是因为$ 仅在串尾寻找其匹配, (或者在串结束前的一行)。如果在包含很多行的串中,用"^" 和"$" 来匹配,, 我们可以使用 /m ("multiline") 选项:

if ($line =~ /(b.*grass)$/m) {}

现在程序会把如下的信息输出:

found browngrass

类似地,句点可以匹配除了换行符之外的所有字符:

while () {

if (/19(.*)$/) {

if ($1 < 20) {

$year = 2000+$1;

} else {

$year = 1900+$1;

}

}

}


如果我们从文件中读入“1981”,$_ 将包含“1981n”。正则表达式中的句点匹配“8”和“1”, 而不匹配“n”。这里正需要这样做,因为换行符不是日期的组成部分。

对于一个包含很多行的串,我们也许要提取其中的大的块,这些块可能会跨越行分隔符。在这种情况下,我们可以使用 /s 选项,并用句点来匹配除了换行符以外的所有字符。

if (m{(.*?)}s) {

print "Found bold text: $1n";

}


此处,我用了{}来表示正则表达式的起始和结束,而不用斜杠,所以,我就可以告诉 Perl我正在匹配,起始字符为"m",结束字符为"s"。你可以把/s 和/m 选项组合使用:

if (m{^(.*?)}sm) {

# ...

}

总结
有两种方法打开文件:open()函数的特点是快速简捷,而sysopen()函数功能强大而复杂。通过 操作符,可以读入一个记录,$/ 变量可以让你控制记录是什么。如果你打算把很多行的内容读入到一个串中,不要使用忘记/s和/m 这两个正则表达式标记。


B2层 发表时间: 05-08-01 13:26

回复: 286 [unique]   版主   登录
每一位自我陶醉的计算机和音乐爱好者都需要能够操纵 MP3 ―― 娱乐性数字音乐的事实标准。在本文中,Ted 介绍了几种使用 autotag.pl 应用程序管理和操纵(搜索、标记、重命名和注释,等等)MP3 的方法。Ted 向读者详细介绍了此应用程序,描述了 CPAN 模块如何启用该应用程序。
对于现在了解计算机的音乐爱好者而言,操纵 MP3 文件是一项必须具备的技能。虽然其他音乐文件格式已存在并在蓬勃发展着,但本文还是主要讨论 MP3 格式,因为众所周知,它是当今最流行的格式。但是,本文所讲述的一般方法也可用于处理其他允许使用标签(tag)的音乐文件格式。实际上,很多使用标签的文件格式都可以从类似本文中的 autotag.pl 程序中受益。欢迎您提出建议。

本文将一般性地讨论有关 Perl 的问题 ,特别关注 MP3 文件的操纵,并详细介绍了 autotag.pl 应用程序。

尽管已经有了 MP3::Info、MP3::ID3Lib、MusicBrainz::Client 和 AudioFile::Identify::MusicBrainz 模块,而且这些模块可能很有用,但我只使用 MP3::ID3Lib 的主要理由是因为它需要 id31ib 软件(请参阅 参考资料)。虽然 MP3::Info 是纯 Perl 语言编写的而且安装也很简单,但我发现 MP3::Tag 功能更强大。之所以没有使用 MusicBrainz::Client 和 AudioFile::Identify::MusicBrainz,是因为 MusicBrainz 似乎是比 FreeDB 更不全面的已发行 CD 的数据库。在本文的结尾,将向读者介绍 ID3 标签加注模块和曲目信息模块的选择。我经过试验和失败而艰难获得的经验表明, MP3::Tag 和 WebService::FreeDB 是最佳的模块。

虽然 CDDB (Gracenote) 磁盘库非常全面,但我还是没有选择使用它。Gracenote 是一家拥有 CD 曲目列表的专有数据库(只允许对数据库执行搜索,不能大量下载)的公司。在 Gracenote 只拥有 CDDB 的早期,志愿者贡献了这些数据库的相当一部分内容。而 FreeDB 是一个志愿者经过有组织的努力提供的免费、无限制的 CD 曲目数据库。FreeDB 数据库的整个内容都可以下载,无版权限制 ―― 因此,如果您愿意,可以建立自己的 FreeDB 服务器。

我不使用的模块并不是因为这些模块一定不好,因此,如果您喜欢,您可以使用它们。基于个人经验和上述原因,我只是更喜欢 MP3::Tag 和 WebService::FreeDB。实际的读写标签在函数中进行了抽象,因此,如果使用不同的模块读写 MP3 标签,就不需要更改很多内容。

我还应提一下,在 Linux 内部的 xterm 和 Eterm 终端模拟器中,Term::ReadLine::Gnu 模块比默认模块 Term::ReadLine::Perl 能更好地工作。如果您注意到在提示输入期望的文本时出现一些奇怪的行为,那么可能要将其安装在 Term::ReadLine 之上。

MP3 标签的简单介绍
首先有音乐。然后出现计算机。计算机速度很慢且只能发出蜂鸣声。即使使用诸如 PC 扬声器这种很令人悲伤的工具(噢,我真希望成为 Apple 和 Amiga 用户),我们也编写程序生成音乐,在游戏和娱乐中使用。之后,声卡越来越好,办公室里到处发出环绕音响和 THX 认证的扬声器所发出的震耳欲聋的声音。

在硬件发展的同时,也产生了很多声音格式。.mid 适用于 MIDI 音调、.voc、.mod、.wav 等。专有的 MP3 格式(涉及到德国 Fraunhofer 学院拥有的很多专利)随着时间的推移流行开来 ―― 它提供很好的压缩和性能。除 MP3 外还有许多格式,著名的有 Ogg Vorbis,但当今 MP3 似乎仍是音乐存储格式的最佳选择。

MP3 文件的一个优点是可以使用 ID3 标签来加注标签。文件内部是有关它的信息 ―― 通常称为元数据。唱片集、艺术家、曲目名称、注释(使用 ID3 版本 1.1)甚至曲目数量都可以存储在 ID3 标签中,只要不超过特定字符数限制即可。

ID3 版本 1.1 的后续版本是 ID3 版本 2(简称为 ID3v2),除简单性外,后者几乎在所有方面都超过了前者。ID3v2 可以处理多种语言,在每个标签元素中存储任意长的数据,甚至将图片存储为标签的一部分。但不幸的是,使用 ID3v2 要了解到 TALB 是唱片集的名称,TIT2 是曲目数量。Ogg Vorbis 格式要花费很长时间才能识别,其中艺术家元素被称为...等等它吧...ARTIST!(公平地说,这仅仅是一个惯例 ―― Ogg Vorbis 注释是无格式的)。不幸的是,现存的数十亿首 MP3 文件都不能在不损失质量的情况下转换为 Ogg Vorbis 格式或任何其他格式,因此,至少在接下来的 5 年里,您可能发现,我们不仅会使用下个“热门”格式,还要使用 MP3 文件。

我已非常努力地从实际的 ID3 标签中抽取标签作为内容。当时机来临时,修改 autotag.pl 会很容易,因此除 ID3 外,它还处理其他加注标签的格式。



基本的 autotag.pl 函数
我把 autotag.pl 几个功能放在了不同的函数中。首先,contains_word_char() 是一个判断某些文本中是否包含某个词(在 Perl 中是 \w in Perl)中的字符的函数。该函数也会正确地处理未定义的值,尽管在警告打开时,常规表达式在匹配未定义的值时会输出警告信息。该函数是极为有用的,因为它不显示警告信息;为了不使用函数而又达到这个目的,您必须检查是否每次都定义了字符串。

清单 1. contains_word_char() 函数

# {{{ contains_word_char: return 1 if the text contains a word character
sub contains_word_char
{
my $text = shift @_;
return $text && length $text && $text =~ m/\w/;
}
# }}}



接下来是输入例程。这些程序相当长,它们试图处理程序所需要的用户交互的大多数情况。

清单 2. get_tag() 函数

# {{{ get_tag: get a ID3 V2 tag, using V1 if necessary
sub get_tag
{
my $file    = shift @_;
my $upgrade = shift @_;
my $mp3 = MP3::Tag->new($file);

return undef unless defined $mp3;

$mp3->get_tags();

my $tag = {};

if (exists $mp3->{ID3v2})
{
  my $id3v2 = $mp3->{ID3v2};
  my $frames = $id3v2->supported_frames();
  while (my ($fname, $longname) = each %$frames)
  {
  # only grab the frames we know
  next unless exists $supported_frames{$fname};

  $tag->{$fname} = $id3v2->get_frame($fname);
  delete $tag->{$fname} unless defined $tag->{$fname};
  $tag->{$fname} = $tag->{$fname}->{Text} if $fname eq 'COMM';
  $tag->{$fname} = $tag->{$fname}->{URL} if $fname eq 'WXXX';
  $tag->{$fname} = '' unless defined $tag->{$fname};
  }
}
elsif (exists $mp3->{ID3v1})
{
  warn "No ID3 v2 TAG info in $file, using the v1 tag";
  my $id3v1 = $mp3->{ID3v1};
  $tag->{COMM} = $id3v1->comment();
  $tag->{TIT2} = $id3v1->song();
  $tag->{TPE1} = $id3v1->artist();
  $tag->{TALB} = $id3v1->album();
  $tag->{TYER} = $id3v1->year();
  $tag->{TRCK} = $id3v1->track();
  $tag->{TIT1} = $id3v1->genre();

  if ($upgrade && read_yes_no("Upgrade ID3v1 tag to ID3v2 for $file?", 1))
  {
  set_tag($file, $tag);
  }
}
else
{
  warn "No ID3 TAG info in $file, creating it";
  $tag = {
      TIT2 => '',
      TPE1 => '',
      TALB => '',
      TYER => 9999,
      COMM => '',
      };
}
print "Got tag ", Dumper $tag
  if $config->DEBUG();
return $tag;
}
# }}}



惟一一个稍微与众不同的函数是 read_yes_no(),可以给它一个 Y 或 1 的默认参数来使默认值为真,任何其他的参数都会使默认值为假。这样,当用户按下回车键或者空格键时,我可以让 read_yes_no() 函数接受不同的默认值。另外,Backspace 键或 Delete 键将使默认值反转。这段代码不华丽,但很实用。

autotag.pl 的开头部分
应用程序 autotag.pl 以一些初始化例程开始。

清单 3. 初始化

use constant SEARCH_ALL  => 'all';

my %freedb_searches = (
  artist  => { keywords => [], abbrev => 'I', tagequiv => 'TPE1' },
  title  => { keywords => [], abbrev => 'T', tagequiv => 'TALB' },
  track  => { keywords => [], abbrev => 'K', tagequiv => 'TIT2' },
  rest    => { keywords => [], abbrev => 'R', tagequiv => 'COMM' },
      );

# maps ID3 v2 tag info to WebService::FreeDB info
my %info2freedb = (
  TALB  => 'cdname',
  TPE1  => 'artist',
      );

my %supported_frames = (
  TIT1 => 1,
  TIT2 => 1,
  TRCK => 1,
  TALB => 1,
  TPE1 => 1,
  COMM => 1,
  WXXX => 1,
  TYER => 1,
      );

my @supported_frames = keys %supported_frames;

my $term = new Term::ReadLine 'Input> '; # global input



EARCH_ALL 是一个常数,当用户想在任何地方搜寻一个词的时候,比如曲目名、艺术家名等,就会使用它。为了防止有人想把它改为另外某个值,我把它设定为常数,但它也可能已经被硬编码为“all”。

%freedb_searches 散列将 FreeDB 字段映射到有关它们的信息上,包括 ID3v2 标签元素。例如,它说明 FreeDB 怎样称呼那些在 MP3 标签中被称为“TPE1”的“artist”。在该散列条目中的“abbrev”字段被用来定义命令行开关,这样,随后我可以基于 %freedb_searches 信息定义一个 -artist 开关,它可以被简写为 -i 。

%info2freedb 散列将光盘中的所有曲目的 FreeDB 字段都映射到 ID3v2 字段。它们不是 %freedb_searches 中的字段,这是一种不同的映射,它表明,对于一个光盘集的所有曲目而言,“cdname”和 “artists”(也分别被称为“TALB”和“TPE1”)是相同的。

我将用 %supported_frames 散列和 @supported_frames 列表来表示我支持哪些 ID3v2 标签元素。我是从该列表生成了这个散列,而不是从该散列中获得这个列表(解释两者之间的差别离题太远,所以不再赘述)。大规模加注标签时,以及在编写 ID3v2 标签时,都要用到已获支持的框架(我只是修改已获支持的框架而已)。

最后,为了让用户在整个应用程序中输入数据,我创建了一个 Term::ReadLine 对象。

下面,我初始化 AppConfig 选项,这样做虽然加重了我的负担,但是有益的。

清单 4. AppConfig 的初始化

# {{{ set up AppConfig and process -help

my $config = AppConfig->new();

$config->define(
  DEBUG      =>
  { ARGCOUNT => ARGCOUNT_ONE, DEFAULT => 0, ALIAS => 'D' },

  CONFIG_FILE      =>
  { ARGCOUNT => ARGCOUNT_ONE, DEFAULT => 0, ALIAS => 'F' },

  HELP        =>
  { ARGCOUNT => ARGCOUNT_NONE, DEFAULT => 0, ALIAS => 'H' },

  DUMP        =>
  { ARGCOUNT => ARGCOUNT_NONE, DEFAULT => 0 },

  ACCEPT_ALL  =>
  { ARGCOUNT => ARGCOUNT_NONE, DEFAULT => 0, ALIAS => 'C' },

  DRYRUN      =>
  { ARGCOUNT => ARGCOUNT_NONE, DEFAULT => 0, ALIAS => 'N' },

  GUESS_TRACK_NUMBERS_ONLY  =>
  { ARGCOUNT => ARGCOUNT_NONE, DEFAULT => 0, ALIAS => 'G' },

    STRIP_COMMENT_ONLY =>
    { ARGCOUNT => ARGCOUNT_NONE, DEFAULT => 0, ALIAS => 'SC' },

  MASS_TAG_ONLY =>
  { ARGCOUNT => ARGCOUNT_HASH, ALIAS => 'M' },

  RENAME_ONLY =>
  { ARGCOUNT => ARGCOUNT_NONE, DEFAULT => 0, ALIAS => 'RO' },

  RENAME_MAX_CHARS =>
  { ARGCOUNT => ARGCOUNT_ONE, DEFAULT => 30},

  RENAME_FORMAT =>
  { ARGCOUNT => ARGCOUNT_ONE, DEFAULT => '%a-%t-%n-%c-%s.mp3'},

  RENAME_BADCHARS =>
  { ARGCOUNT => ARGCOUNT_LIST, ALIAS => 'RB' },

  RENAME_REPLACECHARS =>
  { ARGCOUNT => ARGCOUNT_LIST, ALIAS => 'RR' },

  RENAME_REPLACEMENT =>
  { ARGCOUNT => ARGCOUNT_ONE, DEFAULT => '_' },

  FREEDB_HOST =>
  { ARGCOUNT => ARGCOUNT_ONE, DEFAULT => 'http://www.freedb.org', },

  OR =>
  { ARGCOUNT => ARGCOUNT_NONE, DEFAULT => '0', },

  SEARCH_ALL()  =>
  { ARGCOUNT => ARGCOUNT_LIST, ALIAS => 'A' },
      );

foreach my $search (keys %freedb_searches)
{
$config->define($search => {
      ARGCOUNT => ARGCOUNT_LIST,
      ALIAS => $freedb_searches{$search}->{abbrev},
      });
}
$config->args();

$config->file($config->CONFIG_FILE())
if $config->CONFIG_FILE();

unless (scalar @{$config->RENAME_BADCHARS()})
{
push @{$config->RENAME_BADCHARS()}, split(//, "\"`!'?&[]()/;\n\t");
}

unless (scalar @{$config->RENAME_REPLACECHARS()})
{
push @{$config->RENAME_REPLACECHARS()}, split(//, " ");
}

if ($config->HELP())
{
print <<EOHIPPUS;
$0 [options] File1.mp3 File2.mp3 ...

Options:
-help (-h)          : print this help
-config_file (-f) N : use this config file, see AppConfig module docs for format
-debug (-d) N      : print debugging information (level N, 0 is lowest)
-dump              : just dump the list of albums and tracks within them
-dryrun (-n)        : do everything but modify the MP3 files
-freedb_host H      : set the FreeDB host, default "www.freedb.org"
-or                : search for keyword A or keyword B, not A and B as usual

-accept_all (c)    : accept all search results for consideration for each file,
                      also accept all renames without asking

-rename_badchars (-rb) A -rb B    : characters A and B to remove when renaming

-rename_replacechars (-rr) A -rr B : characters A and B to replace
                                      when renaming

-rename_maxchars N : use at most this many characters from a tag
                      element when renaming, default: ${\$config->RENAME_MAX_CHARS()}

-rename_replacement X : character to use when replacing,
                      default: [${\$config->RENAME_REPLACEMENT()}]

-rename_format (-f) F : format for renaming; default "${\$config->RENAME_FORMAT()}"
                        %a -> Artist
                        %t -> Track number
                        %n -> Album name
                        %c -> Comment
                        %s -> Song title

-guess_track_numbers_only (-g) : guess track numbers using the file
                    name, then exit

-rename_only (-ro)  : rename tracks using the given format (see
                      -rename_format), then exit

-mass_tag_only (-m) A=X -m B=Y : mass-tag files (tag element A is X,
                                  B is Y), then exit (tag elements
                                  available: @supported_frames)

-strip_comment_only (-sc) : strip comments and URLs, then exit

Repeatable options (you can specify them more than once, K is the keyword):

-all (-a)    K : search everywhere
-artist (-i) K : search for these artists
-title (-t)  K : search for these titles
-track (-k)  K : search for these tracks
-rest (-r)  K : search for these keywords everywhere else

Note that the repeatable options are cumulative, so artist A and title
B will produce matches for A and B, not A or B. In the same way,
artist A and artist B will produce matches for A and B, not A or B.
If you want to match A or B terms, use -or, for instance:

$0 -or -artist "pink floyd" -artist "fred flintstone"

EOHIPPUS

exit;
}

# }}}



是的,所有这些代码都是初始化命令行选项的。通过使用 AppConfig,可以在整个程序中使用和修改这些选项。使用 AppConfig 还有很多好处,不过这些内容超出了本文的范围(有关 AppConfig 的更多信息,请参阅参考资料)。

另外,我还使用 %freedb_searches 散列中的条目来创建合适的配置选项,这样可以使用户和程序员更轻松一些。

在加载配置文件以后,如果用户指定了它,那么就用有意义的默认值来植入字符置换数组和不良字符数组。

最后,处理 -help 开关。注意,通过变量插入不同选项的默认值是如何在帮助文本内打印出来大的。这样就形成了可读性非常强的帮助信息。我总是在增加新的特性之后(但有时候是在之前)立即更新帮助信息。我认为,帮助文本应该和程序的功能同步,否则人们将不理解程序,也不知道帮助文本说了些什么。autotag.pl 程序特别需要更多的文档说明,POD 风格的文档应该比较合适。在您阅读本文时,这样的文档可能已经有了。POD 文档是脚本的一部分,因此下载的 autotag.pl(请参阅参考资料)将包括 POD 文档(如果我已经将它写入的话)。

与 ID3v2 标签相关的函数
get_tag() 函数是 autotag.pl 的基本函数。给出一个 MP3 文件名,它就会根据该文件构建一个散列标签。如果标签只是 ID3v1 标签, get_tag() 函数将会免费把它升级为 ID3 标签(多么好的交易!)。如果没有 ID3 标签,get_tag() 函数将创建一个。而且,get_tag() 知道分别查看 COMM 和 WXXX 元素的 Text 和 URL 子元素。

清单 5. get_tag() 函数

# {{{ get_tag: get a ID3 V2 tag, using V1 if necessary
sub get_tag
{
my $file    = shift @_;
my $upgrade = shift @_;
my $mp3 = MP3::Tag->new($file);

return undef unless defined $mp3;

$mp3->get_tags();

my $tag = {};

if (exists $mp3->{ID3v2})
{
  my $id3v2 = $mp3->{ID3v2};
  my $frames = $id3v2->supported_frames();
  while (my ($fname, $longname) = each %$frames)
  {
  # only grab the frames we know
  next unless exists $supported_frames{$fname};

  $tag->{$fname} = $id3v2->get_frame($fname);
  delete $tag->{$fname} unless defined $tag->{$fname};
  $tag->{$fname} = $tag->{$fname}->{Text} if $fname eq 'COMM';
  $tag->{$fname} = $tag->{$fname}->{URL} if $fname eq 'WXXX';
  $tag->{$fname} = '' unless defined $tag->{$fname};
  }
}
elsif (exists $mp3->{ID3v1})
{
  warn "No ID3 v2 TAG info in $file, using the v1 tag";
  my $id3v1 = $mp3->{ID3v1};
  $tag->{COMM} = $id3v1->comment();
  $tag->{TIT2} = $id3v1->song();
  $tag->{TPE1} = $id3v1->artist();
  $tag->{TALB} = $id3v1->album();
  $tag->{TYER} = $id3v1->year();
  $tag->{TRCK} = $id3v1->track();
  $tag->{TIT1} = $id3v1->genre();

  if ($upgrade && read_yes_no("Upgrade ID3v1 tag to ID3v2 for $file?", 1))
  {
  set_tag($file, $tag);
  }
}
else
{
  warn "No ID3 TAG info in $file, creating it";
  $tag = {
      TIT2 => '',
      TPE1 => '',
      TALB => '',
      TYER => 9999,
      COMM => '',
      };
}
print "Got tag ", Dumper $tag
  if $config->DEBUG();
return $tag;
}
# }}}



set_tag() 函数是 get_tag() 函数的兄弟。它编写 ID3v2 标签,查看 COMM 和 WXXX 框架的子元素。它接受散列引用,比如 get_tag() 函数可能产生的那些散列引用。

清单 6. set_tag() 函数

# {{{ set_tag: set a ID3 V2 tag on a file
sub set_tag
{
my $file = shift @_;
my $tag  = shift @_;
my $mp3 = MP3::Tag->new($file);
print Dumper $tag;
my $tags = $mp3->get_tags();
my $id3v2;

if (ref $tags eq 'HASH' && exists $tags->{ID3v2})
{
  $id3v2 = $tags->{ID3v2};
}
else
{
  $id3v2 = $mp3->new_tag("ID3v2");
}

my %old_frames = %{$id3v2->get_frame_ids()};

foreach my $fname (keys %$tag)
{
  $id3v2->remove_frame($fname)
  if exists $old_frames{$fname};

  if ($fname eq 'WXXX')
  {
  $id3v2->add_frame('WXXX', 'ENG', 'FreeDB URL', $tag->{WXXX}) ;
  }
  elsif ($fname eq 'COMM')
  {
  $id3v2->add_frame('COMM', 'ENG', 'Comment', $tag->{COMM}) ;
  }
  else
  {
  $id3v2->add_frame($fname, $tag->{$fname});
  }
}

$id3v2->write_tag();
return 0;
}
# }}}



print_tag_info() 函数简单地打印输出标签的摘要。不像我在 autotag.pl 程序中的其他地方使用的 Data::Dumper 函数(必须指出,有时没有必要使用),print_tag_info() 函数可以提供漂亮的、面向用户的散列标签元素的打印输出。注意,该函数接受散列引用,而不是实际的文件名。

给出文件名和某些可能的 ID3 标签信息,guess_track_number() 函数和guess_artist_and_track() 函数会尽力工作。注意,guess_track_number() 函数知道曲目的数量很少大于 30。

清单 7. print_tag_info()、 guess_track_number()、和 guess_artist_and_track() 函数

# {{{ print_tag_info: print the tag info

sub print_tag_info
{
my $filename = shift @_;
my $tag      = shift @_;
my $extra    = shift @_ || 'Track info';

# argument checking
return unless ref $tag eq 'HASH';

print "$extra for '$filename':\n";

foreach (keys %$tag)
{
  printf "%10s : %s\n", $_, $tag->{$_};
}
}

# }}}

# {{{ guess_track_number: guess track number from ID3 tag and file name
sub guess_track_number
{
my $filename = shift @_;
my $tag      = shift @_ || return undef;

$filename = basename($filename);  # directories can contain confusing data

# first try to guess the track number from the old tag
if (exists $tag->{TRCK} && contains_word_char($tag->{TRCK}))
{
  my $n = $tag->{TRCK} + 0;    # fix tracks like 1/10
  return $n;
}
elsif ($filename =~ m/([012]?\d).*\.[^.]+$/)
                    # now look for numbers in the filename (0 through 29)
{
  print "Guessed track number $1 from filename '$filename'\n"
  if $config->DEBUG();
  return $1;
}

return undef; # if all else fails, return undef
}
# }}}

# {{{ guess_artist_and_track: guess artist and track from file name
sub guess_artist_and_track
{
my $filename = shift @_;
my $artist;
my $track;

$filename = basename($filename);  # directories can contain confusing data

if ($filename =~ m/([^-_]{3,})\s*-\s*(.{3,})\s*\.[^.]+$/)
{
  print "Guessed artist $1 from filename '$filename'\n"
  if $config->DEBUG();
  $artist = $1;
  $track = $2;
}

return ($artist, $track);
}
# }}}



我使用从 FreeDB 搜索中返回的数据来生成带有适当元素的匿名散列。虽然 WebService::FreeDB 字段和 ID3v2 标签元素之间的映射是试验性的,但它工作得很好。

清单 8. make_tag_from_freedb() 函数

# {{{ make_tag_from_freedb: make the ID3 tag info from a FreeDB entry
sub make_tag_from_freedb
{
my $disc  = shift @_;
my $track = shift @_;

# argument checking
return undef unless $track =~ m/^\d+$/;

# note that the user inputs track "1" but WebService::FreeDB gives us that
# track at position 0, so we decrement $track
$track--;

return undef unless exists $disc->{trackinfo};

return undef unless exists $disc->{trackinfo}->[$track];

my $track_data = $disc->{trackinfo}->[$track];

return {
      TIT1 => $disc->{genre},
      TIT2 => $track_data->[0],
      TRCK => $track+1,
      TPE1 => $disc->{artist},
      TALB => $disc->{cdname},
      TYER => $disc->{year},
      WXXX => $disc->{url},
      COMM => $disc->{rest}||'',
  };

}
# }}}



大规模加注标签、大规模重命名、剥离注释和猜测曲目数量
autotag.pl 的主要功能是识别 MP3 文件。但在这个过程中,往往需要对很多组文件进行小的调整。输入 Four Autotagging Horsemen。

剥离注释是非常简单的过程。我使用 get_tag() 获得散列标签,清空 COMM 和 WXXX 字段,以及使用 set_tag() 将该标签写回。实际上,注释剥离可能已经通过大规模标签加注完成了,但这个函数使用得非常频繁,以至于使我感到有必要为它设置一个独立的选项。

猜测曲目数量也使相当简单的。获取散列标签,在该文件和散列标签上使用 guess_track_number() 函数,请求确认,然后将该标签写回到文件中。

在一系列文件上对多个键(例如 TALB)进行大规模标签加注操作。例如:

autotag.pl -mt "TALB=Best" *.mp3

于是,所有具有 mp3 扩展名的文件都在其 ID3v2 标签中指定了 TALB 值。当您拥有某个艺术家的全部乐曲的目录时,以及希望用该艺术家的名字标记所有这些乐曲时,采用大规模标签加注的方式是非常合适的。只有受支持的标签元素才可以大规模加注标签。再一次进行这样的过程:获取散列标签、进行修改,然后将它写回。这样做目的是使它的维护简单便利。

清单 9. 大规模加注标签、注释剥离和猜测曲目数量

# {{{ handle the one-shot options
if ($config->GUESS_TRACK_NUMBERS_ONLY() ||
    $config->STRIP_COMMENT_ONLY() ||
    scalar keys %{$config->MASS_TAG_ONLY()})
{
foreach my $file (@ARGV)
{
  my $tag = get_tag($file, 1);
  unless (defined $tag)
  {
  warn "No ID3 TAG info in '$file', skipping";
  next;
  }

  next if $config->DRYRUN();

  # delegate stripping comments to the mass tagging function
  if ($config->STRIP_COMMENT_ONLY())
  {
  $config->MASS_TAG_ONLY()->{COMM} = '';
  $config->MASS_TAG_ONLY()->{WXXX} = '';
  }

  if (scalar keys %{$config->MASS_TAG_ONLY()})
  {
  foreach (keys %{$config->MASS_TAG_ONLY()})
  {
    unless (exists $supported_frames{$_})
    {
    warn "Unsupported tag element $_ requested for mass tagging, skipping";
    next;
    }
    $tag->{$_} = $config->MASS_TAG_ONLY()->{$_};
  }
  set_tag($file, $tag);
  }
  else
  {
  my $track_number_guess = guess_track_number($file, $tag);

  next if $config->DRYRUN();

  if (defined $track_number_guess &&
              read_yes_no("Is track number $track_number_guess OK for '$file'?", 1))
  {
    $tag->{TRCK} = $track_number_guess;
    set_tag ($file, $tag);
  }
  else
  {
    warn "Could not guess a track number for file $file, sorry";
  }
  }
}

exit 0;
}
# }}}



噢,该介绍大规模重命名选项了。我之所以将这个问题留在最后,是因为这个问题最复杂。对于每个重命名参数而言,我将标签值中的每个“%”都表示为“{{{%}}}”,因为不这样的话,当后面跟随一个特殊的重命名参数时,“%”字符就可能被曲解。例如,用“100%true”作为曲目名,我们来看一看它如何变成“100%TRACKNAMErue”的,这里 TRACKNAME 是我从该散列标签中获取的曲目名。

大规模重命名也可消除不良的字符,代之以某些带有“_”的字符,以确保文件名合理。最后,除非通过命令行给出 -c(accept_all)选项,否则 autotag.pl 将询问是否可以对文件重命名。

清单 10. 大规模重命名

# {{{ handle the -rename_only option
if ($config->RENAME_ONLY())
{
foreach my $file (@ARGV)
{
  my $tag = get_tag($file, 1);
                # the extra parameter will ask us about upgrading V1 to V2
  unless (defined $tag)
  {
  warn "No ID3 TAG info in '$file', skipping";
  next;
  }

  my %map = (
    '%c' => 'COMM',
    '%s' => 'TIT2',
    '%a' => 'TPE1',
    '%t' => 'TALB',
    '%n' => 'TRCK',
    );

  my $name = $config->RENAME_FORMAT();

  foreach my $key (keys %map)
  {
  my $tagkey = $map{$key};
  my $replacement = '';
  if (exists $tag->{$tagkey})
  {
    $replacement = substr $tag->{$tagkey}, 0, $config->RENAME_MAX_CHARS();
                    # limit to N characters
    if ($tagkey eq 'TRCK' && $replacement =~ m/^\d$/)
    {
    $replacement = "0$replacement";
    }
  }

  $replacement =~ s/%/{{{%}}}/g;
                    # this is how we preserve %a in the fields, for example

  $name =~ s/$key/$replacement/;
  }

  $name =~ s/{{{%}}}/%/g;  # turn the {{{%}}} back into % in the fields

  print "The name after % expansion is $name\n" if $config->DEBUG();

  foreach my $char (map { quotemeta } @{$config->RENAME_BADCHARS()})
  {
  $name =~ s/$char//g;
  }

  print "The name after character removals is $name\n" if $config->DEBUG();

  my $newchar = quotemeta $config->RENAME_REPLACEMENT();

  foreach my $char (map { quotemeta } @{$config->RENAME_REPLACECHARS()})
  {
  $name =~ s/$char/$newchar/eg;
  }

  print "The name after character replacements is $name\n" if $config->DEBUG();


  if ($name eq $file)
  {
  # do nothing
  print "Renaming $file is unnecessary, it already answers to our high standards\n"
    if $config->DEBUG();
  }
  elsif (-e $name)
  {
  warn "Could not use name $name, it's already taken by an existing
                        file or directory $file";
  }
  elsif ($config->ACCEPT_ALL() || read_yes_no("Is name $name OK for '$file'?", 1))
  {
  next if $config->DRYRUN();
  print "Renaming $file -> $name\n";
  rename($file, $name);
  }
  else
  {
  # do nothing
  }
}

exit 0;
}
# }}}



结束语
本文的第 2 部分将讨论 autotag.pl 的主循环,介绍该程序的一般用法。

参考资料

阅读 Ted 在 developerWorks 上的“功能丰富的 Perl”系列中的所有 Perl 文章。


下载 autotag application(为了运行该文件,请重命名为 autotag.pl)。


The CPAN module archive 包含很多有用的 Perl 程序。


Ted 在专有的 CDDB / Gracenote 上选择免费的 FreeDB project 作为后端数据库。


可以在 Open Directory 中找到有关很多不同 音频格式 (包括 MIDI、MP3 和 Ogg Vorbis)的资源。


如果您在使用 Term:Readline:Perl 时曾经遇到过麻烦,请尝试使用 Term:Readline:GNU。


id3lib library 是用于读、写和操纵 ID3v1 和 ID3v2 标签的。


MP3::Tag CPAN module 是用于读 MP3 音频文件的标签的。


Webservice::FreeDB CPAN module 通过搜索关键字从 FreeDB 中获取条目。


MP3::ID3Lib CPAN module 使得您可以在 MP3 文件中编辑和添加 ID3 标签 。


CPAN bundle 安装 MusicBrainz 客户机和所要求的模块。


AudioFile-Identify-MusicBrainz 是 MusicBrainz 服务的另一种 CPAN 接口,是纯 Perl 的 MusicBrainz 客户机实现。


IBM developerWorks 的文章“Thinking XML: Manage metadata with MusicBrainz”讨论了 MusicBrainz 服务的元数据问题。


AppConfig 是用于管理应用程序配置信息的 Perl5 模块。


在 Ted 的专栏“Application configuration with Perl”中可以了解更多有关 AppConfig 的信息。


喜欢玩音响吗?在 IBM developerWorks 的文章“Introducing XHTML + Voice -- IBM's proposal to the W3C on developing multimodal UIs”和“Multimodal applications”中,可以了解更多有关启动声音设备的内容。


您可能希望阅读 IBMalphaWorks 上的文章 Voice Toolkit Preview。


噢,试一下Music Sketcher 吧,这是一个图形化的作曲工具,它是由 IBM Research 的多媒体专家编写的。



关于作者
Teodor Zlatanov 于 1999 年获取了波士顿大学计算机工程系硕士学位。他从 1992 年就开始做程序员,使用 Perl、Java、C 和 C++。他的兴趣在于开放源代码工作,致力于文本解析、3 层客户机-服务器数据库体系结构、UNIX 系统管理、CORBA 和项目管理。可以通过 tzz@bu.edu 与他联系。


B3层 发表时间: 05-08-01 13:26

论坛: 编程破解

20CN网络安全小组版权所有
Copyright © 2000-2010 20CN Security Group. All Rights Reserved.
论坛程序编写:NetDemon

粤ICP备05087286号