通过前两节()的介绍的方法,已经可以通过正则表达式替换完成很多的批量文件修改工作了,不过还有一些限制。看看下面这篇短文(文件名cyj.txt):

重阳节,农历九月初九,二九相重,称为"重九"。汉中叶以后的儒家阴阳观,有六阴
九阳。九是阳数,固重九亦叫"重阳"。民间在该日有登高的风俗,所以重阳节又称"登
高节"。还有重九节、茱萸、菊花节等说法。
唐代诗人沈佺期《九日临渭亭侍宴应制得长字》诗:"魏文颂菊蕊,汉武赐萸囊……,年 年重九庆,日月奉天长"。《旧唐书·王勃传》记载:王勃的《滕王阁序》就是在重阳 节这一天写出来的。当时王勃的父亲担任交趾令,王勃前往探视父亲,九月九日路过 南昌时,洪州牧阎伯屿正在重修的滕王阁中宴请宾客及部属,他想夸耀女婿吴子章 (孟学士)的才气,便事先拿出纸笔请宾客动笔作序,所有的宾客都知道他的用意, 没有人敢作。却不料王勃事先并不知道州牧的用心,于是毫不谦让接过纸笔。州牧原 本心中十分生气,立即派人在旁边看王勃书写,谁知道王勃才气不凡,蓄积已久的心 情完全发泄出来,文章越写越好,当写到"落霞与孤鹜齐飞,秋水共长天一色"的词句 时,忍不住拍案叫绝!王勃从此一举名震诗坛。

文中的双引号都是用的半角的,而且不分左右。如何能把半角的双引号替换为全角并且能正确的使用“”呢?如果熟悉正则表达式的话,应该很容易就能想到使用s/"(.*?)"/\1/g来进行替换。那么,我们尝试以下命令:

Windows环境: perl -p -e "s/\"(.*?)\"/\1/g" cyj.txt

Linux环境: perl -p -e 's/"(.*?)"/\1/g' cyj.txt

上面的命令行中没有指定 -i 参数,所以替换后的结果会直接输出到屏幕上而不是写入文件中,很多时候,我们都需要用这种方法先看看替换结果是否正确。运行后,可以看到屏幕上输出以下替换结果:

重阳节,农历九月初九,二九相重,称为“重九”。汉中叶以后的儒家阴阳观,有六阴
九阳。九是阳数,固重九亦叫“重阳”。民间在该日有登高的风俗,所以重阳节又称"登
高节"。还有重九节、茱萸、菊花节等说法。
唐代诗人沈佺期《九日临渭亭侍宴应制得长字》诗:"魏文颂菊蕊,汉武赐萸囊……,年 年重九庆,日月奉天长"。《旧唐书·王勃传》记载:王勃的《滕王阁序》就是在重阳 节这一天写出来的。当时王勃的父亲担任交趾令,王勃前往探视父亲,九月九日路过 南昌时,洪州牧阎伯屿正在重修的滕王阁中宴请宾客及部属,他想夸耀女婿吴子章 (孟学士)的才气,便事先拿出纸笔请宾客动笔作序,所有的宾客都知道他的用意, 没有人敢作。却不料王勃事先并不知道州牧的用心,于是毫不谦让接过纸笔。州牧原 本心中十分生气,立即派人在旁边看王勃书写,谁知道王勃才气不凡,蓄积已久的心 情完全发泄出来,文章越写越好,当写到“落霞与孤鹜齐飞,秋水共长天一色”的词句 时,忍不住拍案叫绝!王勃从此一举名震诗坛。

仔细观察后发现,有好几处双引号都没有正确替换。第一节中介绍正则表达式参数 g 时提到过,perl –p命令行每次读取文件中的一行进行处理,所以对于跨行的双引号对,上述的命令行就不能正确匹配处理了。

要解决这个问题,需要使用perl的另一个命令行参数 -0 ,它的作用是为perl每次读取文件内容指定一个分隔符,-0 参数后紧跟一个八进制数或十六进制数,若是十六进制数,需要使用前缀x。比如 –040 和 -0x20 都表示使用空格作为分隔符。而 -0 参数有几个特别的用法用得非常广泛,分别是 -0、-00、和 -0777 。单独指定 -0 表示以 null 字符串作为分格符,在处理普通的文本文件时一般用不上它。 -00 表示使用段落模式,也就是每次读取一个段落。-0777 表示一次读取整个文件。上例中的短文,由于不存在跨越段落的双引号对,使用 -00 和 -0777 都是适合的。

不过,就算加上了 -0777 这样的参数,结果确还是和上一条命令一模一样,怎么回事呢?原因是正则表达式s/"(.*?)"/“\1”/g中的句点 . 号不能匹配换行符。使用正则表达式的 s 参数使得句点可以匹配换行符。所以以下命令行可以成功替换短文中所有的双引号:

Windows环境: perl -0777 –p -i.bak -e "s/\"(.*?)\"/\1/gs" cyj.txt

Linux环境: perl –0777 -p -i.bak -e 's/"(.*?)"/\1/gs' cyj.txt

-i.bak 参数前两节中都有说明,这里就不复述了。命令运行后,cyj.txt的内容被修改成如下内容:

重阳节,农历九月初九,二九相重,称为“重九”。汉中叶以后的儒家阴阳观,有六阴
九阳。九是阳数,固重九亦叫“重阳”。民间在该日有登高的风俗,所以重阳节又称“登
高节”。还有重九节、茱萸、菊花节等说法。
唐代诗人沈佺期《九日临渭亭侍宴应制得长字》诗:“魏文颂菊蕊,汉武赐萸囊……,年 年重九庆,日月奉天长”。《旧唐书·王勃传》记载:王勃的《滕王阁序》就是在重阳 节这一天写出来的。当时王勃的父亲担任交趾令,王勃前往探视父亲,九月九日路过 南昌时,洪州牧阎伯屿正在重修的滕王阁中宴请宾客及部属,他想夸耀女婿吴子章 (孟学士)的才气,便事先拿出纸笔请宾客动笔作序,所有的宾客都知道他的用意, 没有人敢作。却不料王勃事先并不知道州牧的用心,于是毫不谦让接过纸笔。州牧原 本心中十分生气,立即派人在旁边看王勃书写,谁知道王勃才气不凡,蓄积已久的心 情完全发泄出来,文章越写越好,当写到“落霞与孤鹜齐飞,秋水共长天一色”的词句 时,忍不住拍案叫绝!王勃从此一举名震诗坛。

最后,再解释一下perl正则表达式的 s 参数和 m 参数。从上例中我们可以看到,s 参数使得句点可以匹配换行符而不带 s 参数则不可以。s 启用了正则表达式的“单行模式”,使的换行符和其他字符一样,可以被句点匹配。 而 m 启用的是多行模式,它影响的不是句点符,而是正则表达式中的另外两个特殊符号 ^$ ,它们分别匹配行首和行尾这两个位置而不是任何字符,不指定 m 参数时,^ 和 $ 分别匹配的是字符串的开始和结尾处,而指定 m 参数后,^ 和 $ 分别匹配每一行的行首和行尾。由于 s 和 m 分别影响不同特殊字符的匹配行为,所以它们可以组合使用,同时指定 s 和 m 参数的结果是句点可以匹配换行符的同时 ^ 和 $ 还能匹配每一行的行首和行尾。

总结:

  1. 使用perl -p -i.bak -e "s/搜索/替换/参数" 文件名 命令行可以对文件名指定的文件进行正则表达式替换处理。源文件备份到相应的.bak文件。
  2. Linux环境下,可直接在文件名中使用通配符指定多个文件。Windows环境下虽然不可以直接在文件名中使用通配符,却也可以通过 FOR 命令达到相同的目的。
  3. 指定政策表达式参数 g 进行全局替换,否则每次替换过程中只会替换匹配到的第一处。(默认情况下,每次替换过程是文件的一行。)
  4. 正则表达式中使用括号()可以进行分组捕获,而使用\1,\2等可以引用捕获到的内容。
  5. perl命令行的 -p 参数实际上等同于一个循环读取文件内容并输出的程序,而 -e 参数后面跟着一个perl语句在每次循环读取文件内容后执行,每次执行后处理过的文件内容被输出。-i 参数使得输出的内容被写回源文件。
  6. s/搜索/替换/参数 还有 s{搜索}{替换}参数 等多种写法,可以避免过多的转义符。
  7. perl的 -0 参数可以指定每次读取文件内容时的分隔符。-00 和 -0777 分别指示perl按段落读取和全文读取。
  8. 正则表达式的 s 参数使得句点 . 可以匹配换行符。而 m 参数使得 ^ 和 $ 可以匹配每行的行首和行尾。这两个参数可以组和使用。

标签:,