1.6 逐个处理字符串的每个字符
时间:2006-12-27 来源:xiaoshengcaicai
1.6 逐个处理字符串的每个字符
1.6.1 问题
你想逐个处理字符串的字符
1.6.2 解决方案
split使用空的模式就可以把字符串分割成单个字符,或者使用unpack函数,如果你只是想要字符的值的话:
@array = split(//, $string); # each element a single character
@array = unpack("U*", $string); # each element a code point (number)
或者在循环里面逐个取出来:
while (/(.)/g) { # . is never a newline here
# $1 has character, ord($1) its number
}
1.6.3 讨论
前面我们已经讲过,Perl的基本单位是字符串而不是字符。很少需要逐个处理字符串的字符。通常情况下使用Perl的一些较高等级的操作符比如模式匹配等来处理问题更方便。比如你可以在7.14这一节里面看到怎么样使用一组替换来得到命令行的每个参数。
用可以匹配空字符串的模式去split一个字符串会得到一组字符列表。这是一个方便的特性如果你是有意为之。但是你也容易这样造成无意之失。举个例子,/X*/匹配所有可能的字符串包括空字符串。你可能会发现一些出乎你意料的其他东西。
下面这个例子打印了字符串"an apple a day"里面的每个字符,按照字符的升序打印。
%seen = ( );
$string = "an apple a day";
foreach $char (split //, $string) {
$seen{$char}++;
}
print "unique chars are: ", sort(keys %seen), "\n";
unique chars are: adelnpy
split跟unpack都是返回一个你需要的数组,如果你不是要一个数组的话,使用一个/g标志的模式匹配可以在循环里面逐个的取出字符:
%seen = ( );
$string = "an apple a day";
while ($string =~ /(.)/g) {
$seen{$1}++;
}
print "unique chars are: ", sort(keys %seen), "\n";
unique chars are: adelnpy
一般来说,如果你发现你正在逐个的处理字符,那么可能还有其他更好的方法。比起用index跟substr或者用split跟unpack,用模式匹配会更加容易。正如下面一个例子所展示的,用unpack函数来计算32bit的校验和比起手算有效率多了。
下来这个例子在一个循环里面计算一个字符串的校验和。有很多其他更好的计算校验和的方法,这里用到的只是一种传统的容易计算的校验和的基础。你如果要一个更强大的校验和你可以使用标准模块Digest::MD5(在Perl 5.8版本以后这是个标准模块,你也可以从CPAN里面取得)。
$sum = 0;
foreach $byteval (unpack("C*", $string)) {
$sum += $byteval;
}
print "sum is $sum\n";
# prints "1248" if $string was "an apple a day"
下面这个方法达到同样的效果,但快多了:
$sum = unpack("%32C*", $string);
下面这个效仿了SysV里面的校验和程序:
#!/usr/bin/perl
# sum - compute 16-bit checksum of all input files
$checksum = 0;
while (<>) { $checksum += unpack("%16C*", $_) }
$checksum %= (2 ** 16) - 1;
print "$checksum\n";
这样使用它:
% perl sum /etc/termcap
1510
如果你有GNU版本的sum程序,你只需要给它一个-sysv的执行选项就可以得到跟上面程序一样的结果。
% sum --sysv /etc/termcap
1510 851 /etc/termcap
像下面这个例子里面的slowcat 小程序,它每次只处理输入字符串的一个字符。它每次打印出一个字符后都会停顿一下,这样它就能慢慢的滚动字符串,让读者有足够的时间去阅读显示的字符串。
Example 1-1. slowcat
#!/usr/bin/perl
# slowcat - emulate a s l o w line printer
# usage: slowcat [-DELAY] [files ...]
$DELAY = ($ARGV[0] =~ /^-([.\d]+)/) ? (shift, $1) : 1;
$| = 1;
while (<>) {
for (split(//)) {
print;
select(undef,undef,undef, 0.005 * $DELAY);
}
}
1.6.4 参阅
split 跟 unpack functions 在 perlfunc(1) 和 Programming Perl 29 章里面有; 在3.10 这一节里面解释了select函数在定时方面的扩展。