+ -
当前位置:首页 → 问答吧 → 初来论坛,我想写个适合初学者学习用perl做文字处理的教程。大家看这个主意可以吗?

初来论坛,我想写个适合初学者学习用perl做文字处理的教程。大家看这个主意可以吗?

时间:2010-09-13

来源:互联网

我本人是做硬件布局布线设计的,但是在工作中有需要用perl来处理文本,我就一边摸索,一边记录。
    到现在,有一个星期了。突然想到,是否有人和我是一样情况,所以我想把自己的学习心得记录下来。方便后来的初学朋友查阅。
    我想在这个贴上,不断地把自己学到和使用的编写脚本和心得发上来。
    目的是让没有写过程序的朋友,也能用perl完成自己想做的文字替换。
我也是新手,肯定会有无法完成的问题,一会也会列出,希望高手朋友们一起来解决。
这个帖子,就作为贴心得,收集需求,解决问题的帖子。方便大家查询
最后教程完成之后,我会进行后期制作,做成电子书上传。
各位如果有意愿和我一起写这本书,欢迎加我QQ26440031,或者QQ邮箱[email protected]
把希望加进去的内容,或者自己的心得,发给我。或者在本贴留言。
我一定尊重各位作者的劳动成果,会标明每个部分的作者。
希望这本书编成的时候,各位作者,也能和大家一起分享知识和收获。

OK,闲话不说,我先总体介绍下本书编写的目的
第一:主要对象是,从来没有接触过perl,但对自动大批量文字替换工作有需求的朋友。
第二:介绍对象,主要针对perl的文字处理脚本。后期根据大家需要,也会加上网站文字处理的相关内容。
第三:教程主要方式,通过一个一个完整的脚本程序例子,从简单到复杂,让朋友们学了就能用。逐步掌握perl文字处理脚本
第四:出现的程序例子,要经过测试,最好也能贴出要被修改的原文件,方便新手检测,学习。
第五:本教程避开繁杂的基础知识讲解,只对脚本出现的各参数,进行讲解,以便让零基础的同学也能读懂。
第六:拓展perl的使用人群,让更多的人了解perl,了解编程。让专业和非专业人士,都能享受编程带来的方便。

好了,前言就先说这么多,下面的几贴,我会列出目录,开始书写。对了,特别要强调的是,我也是新手,
非常欢迎各位高手来指点。我只是想做一个让很多和我一样的新手,去享受perl编程的参考资料。

作者: xixizhihua   发布时间: 2010-09-13

本帖最后由 xixizhihua 于 2010-09-13 13:37 编辑

写在前面:所有程序,默认在LINUX在运行
一点见面礼:
  各位朋友,如果你只是想写一个一次可以替换掉目标文件夹里面,很多文档中的同一个内容,那么在终端中,输入以下命令就可以了。希望大家喜欢。
$perl -p -i -e 's/hello/loveperl/g' xx.*.v
这个命令是将你这个文件下 所有xx开头的文件.v文件里,所有的hello全部替换为loveperl.
下面是详细讲解
-p 是告诉电脑,你要把替换完的文件写出来
-i 这是备份文件的选项,以后,我会告诉大家,如果对原文件进行备份。这里默认是不备份
-e 是告诉电脑,这是个脚本语句。
's/hello/loveperl/' 这个是替换语句,s表示替换,前面两个/间,是要替换掉的内容,后面两个/间,是你要替换成的内容。
最后的xx.*.v表示把所有xx开头的所有.v文件替换掉
如果你想替换所有的文件,你可以改成 *.* 这样就行了。(当然也有其他改法,朋友们可以补充)
好了,前言就这么简单。各位朋友,可以回去尝试改改这个命令中参数。
看看你的目标文件变了没变?
测试文件参考
文件名:xxloveperl.v
内容:
xxhello,loveperl,loveperl,hello,goodidea,help
替换后为:
xxloveperl,loveperl,loveperl,loveperl,goodidea,help

作者: xixizhihua   发布时间: 2010-09-13

目录:
第一章:perl处理文字总体介绍
第二章:处理实例
1.单文件简单替换。
2.单文件,带目标判定的替换。
3.单文件,添加替换记录标识
4.单文件,添加额外字符操作
5.多文件处理方法
6.正则表达式的一些高级应用
7.如何替换扩展名
第三章:网站文字替换操作(待高手补充)

正文:
第一章 perl处理文字总体介绍
  为什么我们要用perl处理文字,在我看来主要有三个原因。第一,LINUX大都支持这个软件,现成就有。
第二,它的替换效率比较高,占得内存比较小。第三,不算太难学。(我争取让大家从0,就能掌握)
  perl脚本文字处理,有个比较独特的特性。它是处理一行,输出一行,所有占内存很小。(当然,你可以用join连接操作,把它全读进来,也可以跨行操作,不过,这些都不是主要行为。)
  通过以上几个特性,我们要注意的是,把你的操作,想象成一行一行的处理,来思考你的程序,这样,你的编程才能和perl的行为相符合。
  OK,对于其它的特点,我也说不出来了,下面进入处理实例部分。

作者: xixizhihua   发布时间: 2010-09-13

第二章:处理实例
   特别注意:perl的程序,可以不需要扩展名。但是在运行前,要在终端里的程序所在目录中
   用$chmod a+x 文件名
   然后再用./ 文件名  来执行该文件。
   这个操作,来说明,该文件是可执行文件,否则直接用./ 文件名,会显示权限不够。
1.单文件简单替换
  本章主要以单文件简单替换,来介绍一下,用perl写的文字处理脚本,大概的结构。
  #!usr/bin/perl
  open IN "<xxloveperl.v";
  open OUT ">xxwork1";
  select OUT;
  While (<IN>) {
  if (S/hello/loveperl/) {
  print $_;
  next;
  }
  print $_;
  }
这个程序,就是将xxloverperl里的hello全部换成loveperl。再输出到xxwork1,原程序内容不变。
下面是详细讲解:
第1行 #!usr/bin/perl
  表示这个文件是perl脚本,要用perl编译。个人机器的具体路径可能会有不同,你可以在命令行里,输入which perl来得到路径,大部分机器里,都是默认以上路径的。
第2行 open IN "<xxloveperl.v";
  表示把xxloveperl.v的内容读到IN里面。(一般我们不会直接去改文件)
第3行 open OUT ">xxwork1";
  表示把OUT里的内容,写到xxwork1里面。
第4行 select OUT;
  表示你要开始对OUT的内容进行操作了。
第5行 While (<IN>)  
  表示开始逐行读入IN里面的内容,开始处理。(注,此时每行IN里的内容,默认读入变量$_)
  直到IN里的内容没了,循环结束。
  后面的“{”,表示主循环体开始。(编程基础,每个循环体,都要在判定条件后,用{}括起来。
第6行 if (S/hello/loveperl/) {
  表示,如何找到了hello,并替换成了loveperl就执行下面的操作。
  “{”表示子循环体开始
第7行 print $_;
  表示,$_的内的内容写到OUT里,(此时$_内是替换好的内容)
第8行 next;
  表示这个循环结束,上级循环开头。
  “}”表示子循环体结束
第9行 print $_;
  表示,如果没有完成替换,就把$_内本来的内容写到OUT(也就是没替换过的内容)
  “}”表示整个循环体结束
  这样,经过不断地循环,IN里的文件要么全部被替换后写入OUT,要么直接写入OUT,当全部IN的内容被读完后,整个循环结束。程序把OUT里的内容写到xxwork1里面。你打开文件夹就能看见啦。
   
测试文件参考
文件名:xxloveperl.v
内容:xxhello,loveperl,loveperl,hello,goodidea,help
替换后
原文件:xxloveperl.v
内容:xxhello,loveperl,loveperl,hello,goodidea,help(内容不变)
生成的文件:xxwork1
内容:xxloveperl,loveperl,loveperl,loveperl,goodidea,help(替换完成)

各位朋友,自己动手试试看,成功了吗?成功后,记得要回顾下今天的基本内容。
因为接下来的课程,要进行更复杂的替换哦。

作者: xixizhihua   发布时间: 2010-09-13

本帖最后由 xixizhihua 于 2010-09-13 13:03 编辑

2.条件判断文件替换
  大家好,现在我们开始第二课,条件判断文件替换。
  很多时候,一篇文档中,并不是所有的字,都要全部替换的,是否替换,往往取决于上下文的条件。
  举个例子,我们有个文档,内容如下:
  hello
  goodmorning
  I like the weather
  monday (
  .one (shoping)
  .two (washing)
  .three (cooking)
  .four (sleep)
  );
  I eat apple
  monday (
  .one (working)
  .two (party)
  .three (mom is coming)
  .four (have a lunch)
  );
  Saturday (
  .one (football)
  .two (swiming)
  .three (see the doctor)
  .four (go home)
  );
  I want to sleep

  要求是,我想把所有monday改成workday,并把其下的.one .two .three .four .
  换成first .second .third .addition 后面括号内的内容不变,而Saturday下的一切内容不变。
  那我该怎么做呢?
  下面是实现的程序:最早的源码来自网友javaxiaobai,向他表示感谢。
#!/usr/bin/perl -w
open IN "<xxlist.v";
open OH ">xxwork2";
my $flag = 0;
while (<IN>) {
  my $line = $_;
  if ( $line =~ s/monday/workday/ ) {
  $flag = 1;
  }
  if ( $line =~ /\);/ ) {
  $flag = 0;
  }
  if ( $flag ) {
  $line =~ s/\.one/.first/g;
  $line =~ s/\.two/.second/g;
  $line =~ s/\.three/.third/g;
  $line =~ s/\.four/.addition/g;
  }
  print OH $line;
}
  与第一节相同的内容,我就不重复了。
  这里对新内容进行讲解
  第一,这里引入了一个开关变量$flag,当完成替换monday变workday时,flag为1,子标题.one .two等等的替换循环开始进行。当发现“);”时,从文件可以发现,这一天的list结束了,这时,把flag置成0,子标题替换关闭。
  这是完成条件判断,最简单的循环,也比较好理解。(当然,这个方法不唯一,以后会有介绍)  
  第二,对于特殊字符"."以及")" 、";",这些字符,有本身的程序符号含义,所以要替换的话,一定在之前加个"\"表示转义,这样,程序就会把它们当成普通符号处理了。
  第三,用my $line = $_;
  是为了给默认变量$_一个比较好记得名字,所以用$line,你要嫌麻烦,全用$_也行,不过这样不好记。
   
测试文件参考
文件名:xxlist.v
内容:如例子中所述
生成的新文件名:xxwork2
内容: hello
  goodmorning
  I like the weather
  workday (
  .first (shoping)
  .second (washing)
  .third (cooking)
  .addition (sleep)
  );
  I eat apple
  workday (
  .first (working)
  .second (party)
  .third (mom is coming)
  .addition (have a lunch)
  );
  Saturday (
  .one (football)
  .two (swiming)
  .three (see the doctor)
  .four (go home)
  );
  I want to sleep

  朋友们,实验成功了吗?今天的内容主要是条件开关的控制,转义符号,变量重命名。大家要熟悉它。
下次课程,我将介绍一个,帮助你检查替换工作是否正确的小技巧。

作者: xixizhihua   发布时间: 2010-09-13

3.添加替换次数记录
  朋友们,我们都希望自己做的脚本,对文件的替换正确无误。但毕竟程序时人编的,很多时候,我们还是会犯错,因此,对修改后的文本做检查,来看看自己脚本编辑是否正确,是很必要的。
  那么,纯手工检查比较累,有什么方法可以省点力气呢?
  经过实践,我发现,对于一些熟悉的文本,我可以先统计出,我要做多少处替换,或者各种替换的数量关系,比如,上一课的例子中,就应该所有替换都完成了2次,且各替换数量相等。
  因此,我对文件进行小小的改动,就能实现在文件最后,告诉用户,这次脚本进行了多少次各类替换操作。
  还是用上节课的替换例子,xxlist
  这次,要加入统计替换次数功能,代码如下:
  #!/usr/bin/perl -w
open IN "<xxlist.v";
open OH ">xxwork3";
my $flag = 0;
my $rpday = 0;
my $rpone = 0;
my $rptwo = 0;
my $rpthree= 0;
my $rpfour = 0;
while (<IN>) {
  my $line = $_;
  if ( $line =~ s/monday/workday/ ) {
  $flag = 1;
  $rpday ++;
  }
  if ( $line =~ /\);/ ) {
  $flag = 0;
  }
  if ( $flag ) {
  if ($line =~ s/\.one/.first/g){
  $rpone ++; }
  if ($line =~ s/\.two/.second/g) {
  $rptwo ++; }
  if ($line =~ s/\.three/.third/g) {
  $rpthree ++; }
  if ($line =~ s/\.four/.addition/g) {
  $rpfour ++; }
  }
  print OH $line;
}
  print "\//";
  print "rpday = $rpday ";
  print "rpone = $rpone ";
  print "rptwo = $rptwo ";
  print "rpthree = $rpthree ";
  print "rpfour = $rpfour ";

  程序讲解:
第一组:my $rpday = 0;
  my $rpone = 0;
  my $rptwo = 0;
  my $rpthree= 0;
  my $rpfour = 0;
  这5个行表示,分别设定了5个记录替换次数的变量,并把它们初值设定为0.对应关系,可以在程序中看到。

第二组:$rpday ++;
  这个是程序中的自+1,表示如果if的条件达成,就让这个变量+1。
  说简单点,就是替换成功了,记录这个替换次数的变量就增加1.完成了计数功能。
  后面的相同结构,都是同样功能

第三组:print "\//";
  print "rpday = $rpday ";
  print "rpone = $rpone ";
  print "rptwo = $rptwo ";
  print "rpthree = $rpthree ";
  print "rpfour = $rpfour ";
  这一组程序的功能,是打印“//变量名1 = 替换次数 变量名2 = 替换次数 …… ”
  这样,你就能在文件最后,看到你各替换操作,都进行了多少次了。是不是很方便呢?
   
  注意:这次增加的功能很简单,就是给每个替换设置了一个变量,相应替换每进行一次,变量就自+1.
  最后按格式把变脸替换次数打印出来即可。"//"用在程序中,表示注释,这样,你的替换次数不会影响程序文件的应用。还可以方便以后检查。
   
  测试文件参考
文件名:xxlist.v
内容:与上一节中一样
生成的新文件名:xxwork3
内容: hello
  goodmorning
  I like the weather
  workday (
  .first (shoping)
  .second (washing)
  .third (cooking)
  .addition (sleep)
  );
  I eat apple
  workday (
  .first (working)
  .second (party)
  .third (mom is coming)
  .addition (have a lunch)
  );
  Saturday (
  .one (football)
  .two (swiming)
  .three (see the doctor)
  .four (go home)
  );
  I want to sleep
//rpday = 2 rpone = 2 rptwo = 2 rpthree =2 rpfour =2

  朋友们,实验成功了吗?今天的内容回顾一下,其实很简单。就是每次替换结束后,让替换变量+1,在文件的最后,先打//,再把所有的标识变量打印出来。
  虽然这个标识比较简单,不过,当你做检查的时候,相信会体会到他带来的方便的:)
  好了,今天的课程到这里,下面一章我们会讲解如何在文件中的目标地点,添加文本。大家要及时复习哦。

作者: xixizhihua   发布时间: 2010-09-13

这两天在写下面的教程时候,实现需到些问题,主要是对正则里的变量替换很不熟,我先想办法搞定,稍后发出:
欢迎高手也帮我想想哦。
基本问题是
monday (
  .one (working)
  .two (party)
  .three (mom is coming)
  .four (have a lunch)
  );
1)第一个操作是:如果出现日期名为monday,就把名字替换成workday,其下的分项中.two (原来内容) 替换成 .second (~原来内容)。 且.two 与(原来内容)间的空格数不定。而Saturday下的一切内容不变。
  例如:所有的monday替换成workday,并且把其下的.two (washing) 替换为 .second (~washanging)
  也就是分项的名字替换掉,后面()里的内容,加一个“~”。
  如何忽略中间的空格数,进行匹配替换。
  书上说,好像可以用()把要替换的两部分内容记录到变量里,中间用*,然后再替换变量。不过,我一直没弄成功,总有问题,希望高手写条替换范例。
2)第二操作,是做批处理替换,我原来做的都是用Perl饼,除了Perl -i -p -e (……),进行简单替换。
用到这里就不会了。。,
如果要把perl饼写成完整程序,该怎么弄呢?我想把上面的操作,变成对于一个文件夹内所有文件,都执行这个操作。
  以上2个问题,如果有高手知道,希望告诉我下哦。谢谢啦。

作者: xixizhihua   发布时间: 2010-09-13

学习还是循序渐进的好。

作者: flw   发布时间: 2010-09-13

恩,flw版主,说的对。干哪行都得循序渐进。
不过,我们这些外行,确实有需求想用perl编文字处理的脚本。
但因为本身有硬件设计的任务压力,又没有那么多时间,从0学起。(其实leraning perl我也看了不少时间,但遇到有的问题,还是解决不了。)
我就想,能不能用一个一个的脚本例子,把一些常用内容串起来。
让我们没编过程的,也能使用perl。
就像我刚才提出的两个问题,或许高手解决只要几行代码,再加点解释,告诉我们下,大概是做什么用的,我们外行就可以学来用。
但要我们自己摸,或许得摸好长时间。
所以,我才基于这个目的,开了这个贴。
希望请高手帮我解决问题的同时,也能让其它新人做参考,让大家觉得,perl编程,不是那么高不可攀。
还有一个目的,就是,很多实现方法,都是我自己想的,可能很笨。
或许perl高手有更简洁有效的实现,也希望大家能提出来。再次感谢。

作者: xixizhihua   发布时间: 2010-09-13



QUOTE:
$perl是告诉电脑,这个命令是perl命令,要用perl编译器。


学习真不应该是这个样子。

作者: flw   发布时间: 2010-09-13

。。。。想当然了....我错了,版主说的对.
多谢版主,我已把错误的那句删掉了。
版主,能不能麻烦您,解答下,我7楼的问题。
已经写出的部分,如果还有错误,我一定立刻改正。
麻烦您费心了

作者: xixizhihua   发布时间: 2010-09-13