网站开发日志

2009年5月11日星期一

Perl - 读取某个路径下的文件

Perl在文件系统处理方面功能强大!下面要介绍如何对某个路径下的文件进行读取。其实用Perl来实现这一功能的方法很多,但是我觉得下面的这种方法还是挺简单的。

注释应该挺详细了。时间宝贵,这里不做详细介绍,如有问题请留言。

#!/usr/bin/perl -w
my @files = <*>; 
#
# <*> 很重要,也很强大!
# <*> 相当于把当前默认下的所有非隐藏文件文件抓出。
# 如果你要抓取其他路径,或者过滤某些文件类型,你可以这么写:
#</home/dcai/*.txt> 相当于把/home/dcai下的所有.txt文件抓出,这时抓取的文件含有绝对路径。
#
foreach $file (@files) {
#可选:对获取的文件名处理,例如获取perl文件的名称(不包含扩展名'.pl')
   my $filename = "";
   if($file =~ m/.*\/(.*)\.pl$/){
      $filename = $1;
   }
   elsif ($file =~ /(.*)\.pl$/){
      $filename = $1;
   }
   print $filename, "\n";
   
   my $whole_file = "";
#读取文件内容:
   open(FILE, "< $file") or die "can't open $file: $!";
   while(<FILE>){
      $whole_file .= $_;
   }
#打印文件内容:
   print $whole_file, "\n";
   close(FILE);
}

标签:

2009年4月27日星期一

Ubuntu下安装Perl Module

今天在Ubuntu下安装Perl Module - Wordpress-API,准备用来自动从远端发布日志到Wordpress。从命令行安装Perl Module,对初学者来说还是比较麻烦的。如果你按照cpan.org提供的Module安装指南,往往是安装不成功的, 因为每一个Module一般都有是在别的Module基础上创建的,就比如说,Woerpress-API module, 它需要 Carp  |   Cwd  |   Date::Manip  |   Exporter  |   File::Type  |   LEOCHARRE::DEBUG  |   MIME::Base64  |   Smart::Comments  |   Test::Simple  |   WordPress::XMLRPC  |   YAML 所以你必须先安装这些Prerequisites。而这些相关Module建立在别的Module上。头大了吧?

还好我们有一些自动安装Perl Module的工具可以用,例如:Windows下,可以用Activate Perl Module Manager来安装。而在Ubuntu命令行下,你可以这么来:

A) 启动CPAN Shell:

sudo perl -MCPAN -e shell
#不是很清楚是否需要sudo,但是我觉得有些模块可能需要sudo才可以安装。

B) 升级CPAN(可选)

cpan[1]> install CPAN
#注意:'cpan[1]>'是命令提示符

C) 安装你要的Module

cpan> install Wordpress::API
#注意:这里Wordpress::API是Module名,你可以到CPAN上找到Module名

注意:这个安装过程是交互式的,所以你要根据提示,输入'yes'。如果安装出错,会有提示:Not OK/without success。一般安装成功,你会看到:

/usr/bin/make install  -- OK

标签:

2008年10月27日星期一

Perl 的排序问题

Perl自带有排序函数sortsort的用法主要有两种:

1. 直接使用

@articles = sort @files;

上述例子中,sort将依据标准的字符串排序法则对数组files里包含的元素进行排序。也就是说数组里定义的数字或者字母将被转换成ASCII码后进行排序。

2.自定义排序法则

@articles = sort {sub rule} @files;

上述例子中的"sub rule"指的是自定义排序法则的子程序(subroutine)。

在介绍自定义排序法则的子程序之前,让我们先来看看perl里的比较符(comparison operators)

  • 数字比较符: <, >, <=, >=, ==, <=>
  • 字符串比较符:lt, gt, le, gr, eq, cmp

前面几个很容易懂,最后一个"<=>"或者"cmp"是用来比较两个数字或者字符串之间的关系。

$a 和 $b 之间的关系 $a <=> $b 的返回值
$a 大于$b 1
$a 等于$b 0
$a 小于$b -1

因此,常用的自定义排序法则的子程序可以这样写:

#数字比较
@sorted = sort { $a <=> $b } @not_sorted 
#字母比较1
@sorted = sort { $a cmp $b } @not_sorted
#字母比较2, 先转换成小写比较
@sorted = sort { lc($a) cmp lc($b) } @not_sorted

这里的$a 和$b其实是perl固有的全局变量(build-in/package globals) , 我们把自定义排序法则的子程序写成: {$a <=>$b} ,perl编译器将得知你定义了的排序法则是采用数字比较大小。我们在对hash按key进行排序时候常用到自定义排序法。例如:

foreach(sort{$mapword{$b}<=>$mapword{$a}}keys %mapword){
print "$_$mapword{$_}\n";
} 
#1. 'keys % mapword' 得到的是一个数组
#2. 用全局变量,$a, $b定义了对比法则,将对%mapword的key数值按从小到大顺序排列
#3. 打印出$key值和对应的hash值

既然是自定义法则,你还可以把比较法则定义成你自己想要的,比如说,你要把一组数安字母顺序排列,但是你要让dh永远排在前面,你可以这么写:

@words = ("hi", "da", "abc", "dh", "man");
@sorted = sort { 
                 if ($a eq 'dh') { return -1; }
                 elsif ($b eq 'dh') { return 1; } 
                 else { return $a cmp $b; } 
               } @words;
print @sorted;
#打印结果为:dhabcdahiman

标签:

2008年10月25日星期六

Perl 文件重命名批处理

经过一个多月的努力,终于把公司的新站做好了。前几天我已经把代码从开发服务器拷贝到主服务器上了,只是用了不同的文件名。今天早上8:30开始割接 - 让网站使用新的代码。为了让转换的过程更加迅速,我编写了Perl脚本来代替手动文件备份和重命名。

具体要完成的工作是:

  • 1. 从指定根目录开始搜索新的代码文件,这写新的文件命名规则为:filename_new.tpl, 也就是在实际文件后加了"_new"。
  • 2. 备份原有文件, 命名规则:filename_bak.tpl
  • 3. 把新文件重命名,filename_new.tpl » filename.tpl

因为需要处理的文件分布在不同的路径下面,而且路径也分好几级,所以我用到了Perl的Find模块。具体代码如下:

#!perl -w
use strict;
use File::Find;
my %directories;
my @newfiles;
find( \&filehandler, "/var/www/smarty/" );
for my $file_new ( sort keys %directories ) {
    my @dirlist = @{$directories{ $file_new } };
    (my $file_ori = $file_new) =~ s/_new//;
    (my $file_bak = $file_new) =~ s/_new/_bak/;
    for (@dirlist){
       my $dir = $_;
       if (-e "$dir\/$file_bak"){
         die "$dir\/$file_new was renamed";
       }
       else{
         if(rename "$dir\/$file_ori", "$dir\/$file_bak"){
            print "$dir\/$file_ori is backup\n";
         }
         else{ warn "!ERROR: rename $dir\/$file_ori failed\n";}
         if(rename "$dir\/$file_new", "$dir\/$file_ori"){
            print "$dir\/$file_new is renamed to $file_ori\n\n";
         }
         else{ warn "!ERROR: rename $dir\/$file_ori failed\n";}
       }
    }
}
sub filehandler {
    #只查找文件,跳过路径
    return unless -f $File::Find::name;
    # Just take the filename
    my $nameonly = $_;
    return unless $nameonly =~ m/.+_new\..+$/i;
    #push (@newfiles, $nameonly);
    $directories{ $nameonly } = [] unless defined $directories{ $nameonly };
    push( @{$directories{ $nameonly } }, $File::Find::dir );
}

以上代码仅供参考,如果你要完成类似的工作,要根据你自己的情况修改代码。以上代码用到Perl的Array,Hash,正则表达式等知识点。请您确保对代码完全理解后使用,有任何问题你可以写信给我。但是我不对由于代码的误用造成的数据丢失负责,请慎用!!!

标签:

2008年10月4日星期六

Perl Regular Expressions (Perl 正则表达式)

我现在主要用PHP来写代码,当用到Regular Expressions的时候,就想到了我以前常用到的Perl Regular Expressions。虽然二者是使用方法所不同的,但是字符串匹配的规则是大体相通的,而且PHP兼容Perl的匹配规则(Pattern)。因此,我想先把Perl的正则表达式回顾总结一下。(参考:Steve Litt's Perls of Wisdom)

正则表达式的作用:

1. 判断字符串是否和一个特定字符或字符串匹配

  • $string =~ m/sought_text/;  #如果这个表达式成立,那么$string里就含有特定字符'sought_text'。

2. 选择字符串里的特定部分

  • $string =~ m/whatever1(sought_text)whatever2/;
  • $soughtText = $1;  #如果上面的表达式成立,那么$1就是'whatever1'和'whatever2'之间的那部分

3.字符串替换/翻译

  • $string =~ tr/originaltext/newtext/; 把字符串里'orignialtext'用'newtext'来代替

常用通配符和重复方法:

.   匹配任意字符 (除了换行符"\n")
^ 表示字符开始的空字符
$ 表示字符结束的空字符
\w  匹配任意(文字和数字)字母和下滑线
\W  匹配非任意(文字和数字)字母和下滑线的字符
\s  匹配空格符
\S  匹配非空格符

\d  匹配数字
\D  匹配非数字
\t  匹配'tab'键值
\n  匹配换行符
\r  品牌回车键
*      匹配0次或者多次
+      匹配一次或者多次
?      匹配0次或者一次
{n}    刚好匹配n次
{n,}   匹配n次以上,包含n
{n,m}  匹配n次以上,但是不超过m次

例子:

$string =~ m/\s*rem/i;   
#如果以上表达式成立,那么$string可以是以任意个空字符开始然后紧跟着非空字符可以为
#'rem'或者'REM'(不分大小写)
$string =~ m/^\S{1,8}\.\S{0,3}$/;
#以上表达式用来匹配1到8个的非空字符紧跟着一个'.',然后再紧跟着0-3个的非空字符。
#比如说'abcd.', 'a.34s',但是不匹配'.12dg','.123',等等。
匹配组, group()
如果我们想对一组字符进行匹配,我们可以用()。
例子:
if($string =~ m/(A|E|I|O|U|Y|a|e|i|o|u|y)/)
  {print "String contains a vowel!\n"}
#以上判断字符串是否包含元音
if($string =~ m/^(Clinton|Bush|Reagan)/i)
  {print "$string\n"};
#以上判断字符串是否已Clinton,Bush,Reagan中一个开头,不分大小写

匹配单一字符,character classes []

如果我们想匹配一组字符的中一个,我们可以用[]。

例子:

if($string =~ /[AEIOUYaeiouy]/)
  {print "String contains a vowel!\n"}
#这等同于上面的提到的组匹配,但是你会发现用'[]'要简介许多
if($string =~ m/(A|E|I|O|U|Y|a|e|i|o|u|y)/)
  {print "String contains a vowel!\n"}
if($string =~ /[^AEIOUYaeiouy]/)
  {print "字符串没有包含任何元音字母"}
#注意:上面这个例子中'^'不是表示起始空字符,而是表示'非'
if($string =~ m/^[A-E]/)
  {print "字符串是以A-E中的任何一个字母开头的\n"}
#注意:上面这个例子中'^'表示起始空字符

字符串替代 s

例子:

$string =~ s/少儿上网/61dh\.com/;
#把字符串里的'少儿上网'用61dh.com来代替。
注意:因为'.'表示任意字符,所以当我想表示'.'这个特殊字符时,可以用'\.'来表示。

字符串翻译 tr

和字符串替代比较相似,但是也有它独特的地方:把一组字符串的字符一一对应的替换。

例子:

$string =~ tr/[a-z]/[A-Z]/;
#把小写字母转换成大写字母
$string =~ tr/[六,一,导,航]/[6,1,d,h]/;
#把'六一导航'翻译成'61dh'

贪婪匹配和不贪婪匹配

Perl的正则表达式通常是贪婪匹配的,也就是说它尽量匹配更多的字符。这么说比较抽象,让我们看例子:

my($text) = "mississippi";
$text =~ m/(i.*s)/;
print $1 . "\n";
#'*'表示匹配0个或者多个,因为Perl比较贪心,所以匹配结果是’ississ'
$text =~ m/(i.?s)/;
print $1 . "\n";
#'*'表示匹配0个或者一个,同样Perl尽量去匹配多,所以结果是'iss'

如果想找到字符串里第一次出现'i'和's'的情况。也就是所谓的不贪婪匹配。我们可以同时使用'*'和'?'。

例子:

my($text) = "mississippi";
$text =~ m/(i.*?s)/;
print $1 . "\n";
#这下结果为'is'

补充(11/6/08):需要匹配多行的字符串,可以使用“smi”后缀修饰。例如:

$a = '<{LIST(10011002,10,0,0)}>
<tr>
<td width="100"> </td>
<td> </td>
<tr>
<{/LIST}> ';
if ($a =~ /<{[^}]+}\>(.+)\<{[^}]+}>/smi){
   print "yes";
}

标签: