分类: PERL
2014-01-20 11:34:51
1.11.1 问题描述
希望实现对制表符和相应数量空格的相互转换,即当文件有许多连续的空格时,把空格转换成制表符可以减小文件的大小。有的设备不能识别制表符或者输出的制表符不是期望的位置,这个时候需要把制表符转换为空格。
1.11.2 解决方案
可以通过两种方式实现,一种是看起来有点怪怪的替换方式:
while ($string =~ s/\t+/' ' x (length($&) * 8 - length($`) % 8)/e) {
# spin in empty loop until substitution finally fails
}
或者使用Text::Tabs 模块:
use Text::Tabs;
@expanded_lines = expand(@lines_with_tabs);
@tabulated_lines = unexpand(@lines_without_tabs);
1.11.3 讨论
假设制表符的位置是N位置(习惯上N=8),用替换的方法将制表符转换为空格时很容易的,但是看起来有点难懂(教程中也没有使用Text::Tabs模块)。另外,这种方法使用到了变量“$`”,这个变量会降低模式匹配的速度。这种算法可以用来制作一个过滤器来将输入的制表符扩展为8个空格。
while (<>) {
1 while s/\t+/' ' x (length($&) * 8 - length($`) % 8)/e;
print;
}
为了避免使用变量$`,可以使用稍微复杂的另外一种替换,使用数值变量进行显式的捕捉,下模的例子将制表符扩展为四个空格
1 while s/^(.*?)(\t+)/$1 . ' ' x (length($2) * 4 - length($1) % 4)/e;
另外一种方法是通过使用数组@+和@-,使用偏移量来将制表符扩展为四个空格
1 while s/\t+/' ' x (($+[0] - $-[0]) * 4 - $-[0] % 4)/e;
回顾上面几个while的循环,是否你想知道为什么这个循环不能用简单的s///g代替呢?这是因为我们每次都要从行的开始重新计算长度,而不是从上次的匹配成功开始。
“1 while CONDITION“的用法和”while (CONDITION){}”的用法是一样的,只是较短而已。这种用法起源于之前的Perl第一个循环比第二个循环运行的速度快很多的时候,当然限制第二个循环的速度也相当快了,这只是一个习惯的用法。
标准的库模块Rext::Tabs提供了转换函数实现两种符号的相互转换。用$tabstop变量控制每个制表符空格的数量并且不会降低程序的性能,因为它使用的是数值变量$1,$2而不是$&,$`.
use Text::Tabs;
$tabstop = 4;
while (<>) { print expand($_) }
我们也可以使用 Text::Tabs 但是不扩制表符空格的数量,下面的例子用的是$tabstop的默认值8.
use Text::Tabs;
while (<>) { print unexpand($_) }