1.11 扩展和压缩Tab字符
时间:2007-01-04 来源:xiaoshengcaicai
1.11 扩展和压缩Tab字符
1.11.1 问题
你想把字符串里面的所有tab都转换成一些合适数量的空格,或者反过来把一些空格转换成tab。如果一个文件含有很多连续空格,把一些连续的空格转成一个tab就可以减小文件大小。把tab转换成空格可能是一些设备在产生输出的时候不支持tab这个字符,又或者这个设备对tab字符的理解跟你意料的不一样。
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 讨论
假定tab stops被设定为每N个位置(通常N=8)(译注:tab这个字符的意思实际上是跳到下一个tab stops),要把tabs转换成空格是很简单的。标准的做法并不是使用Text::Tabs这个模块,而是使用一种稍微难以理解的方法:使用变量$`,使用这个变量会让程序里面的正则匹配变慢。这一点在第六章的特殊变量一节有说明。你可以用下面的代码来做一个过滤器扩展输入的tabstops成8个空格:
while (<>) {
1 while s/\t+/' ' x (length($&) * 8 - length($`) % 8)/e;
print;
}
不使用$`的话,你可以用另外一种稍微复杂的方法:用那些保存匹配内容的数字变量。下面这个例子是用4个空格来扩展,不是上面例子的8个:
1 while s/^(.*?)(\t+)/$1 . ' ' x (length($2) * 4 - length($1) % 4)/e;
还有一个方法是直接使用数组@+跟数组@-里面的偏移量。同样扩展成4个空格:
1 while s/\t+/' ' x (($+[0] - $-[0]) * 4 - $-[0] % 4)/e;
你会发现上面的所有代码都是写在while循环里面的,难道就不能一次性的使用一个简单的s///g来实现吗?其实是因为在每次匹配过后我们都需要重新计算从每行开头到目前位置的长度,而不是从最后一次匹配发生位置开始来计算长度。(译注:每替换-次tab成空格,该替换位置之后的所有字符相对于tabstops的相对位置都会发生改变,所以每次只能替换一次tab)。
语句: 1 while 条件 跟这样的代码是一样的:while (条件) { }, 只是比后者短。Perl一开始的时候,前者的运行速度竟然比后者快。现在后者跟前者的运行速度差不多了,只是前者这个用法还是比较方便的,仍然保留着。
Text::Tabs这个标准模块提供了tab跟空格之间的互相转化,该模块导出了一个变量$tabstop,是用来控制每个tab是多少个空格,而且性能上还是不错的,因为使用了$1,$2,而没有用到$&,$`。
use Text::Tabs;
$tabstop = 4;
while (<>) { print expand($_) }
可以使用Text::Tabs 来压缩空格成tabs. 下面这个例子使用了默认的tabstop的值8:
use Text::Tabs;
while (<>) { print unexpand($_) }
1.11.4 参阅
Text::Tabs 这个模块的使用指南(manpage );
perlre(1) 跟perlop(1)里面的s///;
@- 跟@+ 数组变量 (@LAST_MATCH_START and @LAST_MATCH_END)在 Programming Perl 第28章有;
Programming Perl第5章关于 "When a global substitution just isn't global enough" 这一部分。