在日常工作中,你可能会遇到这种情况,你只能用正则表达式(sed或perl的正则表达式)来解决这样一个问题:替换所有不匹配某表达式的输入成为某特殊字符串。特别是某些软件的配置选项只提供了让你使用正则表达式的情况。例如配置“除了某含有域名的URL可以访问外,不能访问其它URL”等策略。本文介绍一种在没有取反正则表达式情况下,如何用三次正则表达式解决这个问题。
典型的例子是电话呼叫策略配置的例子,如请配置“只允许拨打上海电话,不允许拨打任何其它地区电话”策略。实际上要求:被叫电话号码将作为表达式的输入,要求若号码不是以021为前缀,则必须把整个号码转换为字符串bar。如:
被叫号码若为 02188888888 则经处理后,输出还是 02188888888;而诸如02566666666之类所有不以021为前缀的电话号码都要被转化为bar字符串。处理过程只能是正则表达式,且允许多个表达式(前一个表达式的输出为后一个表达式的输入)。
我曾在网上查过,没有找到“不匹配则替换”的方法。我想的方法是用三个正则表达式解决。例子:
echo 02188888888 | sed 's/^/(.*/)$/_t_/1/' | sed 's/^_t_021/021/' | sed 's/^_t_.*$/bar/'
这里只是用Linux命令来与管道演示一下,命令执行的结果将输出02188888888(保持号码不变),步骤说明:
1)第一个表达式 sed 's/^/(.*/)$/_t_/1/' 意思是“在号码前面加一个特殊前缀_t_”,号码02188888888被处理后,第一个表达式将输出 _t_02188888888 。
2)第二个表达式 sed 's/^_t_021/021/' 意思是“若输入的字符串前缀为_t_021,则转化该前缀为021”,此处_t_02188888888 又变回了 02188888888。——感觉有点无聊把,但等会就知道这样做的用处了^_^
3)第三个表达式 sed 's/^_t_.*$/bar/' 意思是“若输入的字符串前缀为_t_,则把整个字符串替换成bar”,此处输入为02188888888,所以本表达式不起作用,最终输出还是02188888888。(即允许拨打该号码)
再看看若输入的是一个非021前缀的号码:
echo 05788888888 | sed 's/^/(.*/)$/_t_/1/' | sed 's/^_t_021/021/' | sed 's/^_t_.*$/bar/'
1)第一个表达式 sed 's/^/(.*/)$/_t_/1/' 处理结果在号码05788888888前加了前缀:_t_05788888888 。
2)第二个表达式 sed 's/^_t_021/021/' 发现字符串_t_05788888888 不是以_t_021为前缀,所以它将不做任何处理,输出的结果还是_t_05788888888 。
3)第三个表达式 sed 's/^_t_.*$/bar/' 发现字符串前缀为_t_,则把整个字符串替换成了bar。OK看明白了吗^_^
OK也许你已经发现,上述表达式只适用于“替换前缀不为XX的字符串”,若要匹配的标识在字符串中间怎么办?比如,如何实现“请替换不含hero的整个字符串为bar”的要求。其实对上面的表达式稍加处理就可以了:
echo 'I am hero yeah' | sed 's/^/(.*/)$/_t_/1/' | sed 's/^_t_/(.*/)hero//1hero/' | sed 's/^_t_.*$/bar/'
将输出 'I am hero yeah' ,而输入其它字符串,如:
echo 'He is not' | sed 's/^/(.*/)$/_t_/1/' | sed 's/^_t_/(.*/)hero//1hero/' | sed 's/^_t_.*$/bar/'
将输出 'bar'。
Perl表达式实现和sed差不多,只是 /(.*/) 之类的只用直接写成 (.*) 就行了。下面是一个测试演示用的perl脚本,大家能看到整个处理过程是怎么样的:
------------------------
#!/usr/bin/perl
sub parse {
my $str = shift;
print "input = /"$str/"/n";
$str =~ s/^(.*)$/_t_/1/;
print "s/^(.*)/$/_t_//1/ => /"$str/"/n";
$str =~ s/^_t_(.*)hero//1hero/;
print "s/^_t_(.*)hero///1hero/ => /"$str/"/n";
$str =~ s/^_t_.*$/bar/;
print "s/^_t_.*/$/bar/ => /"$str/"/n";
print "output = /"$str/"/n";
}
parse("Iamheroyeah");
print "/n";
parse("Heisnotagoodman");
-------------------------------------
用 perl test.pl 来查看 (test.pl为该文件名)输出结果为:
input = "Iamheroyeah"
s/^(.*)$/_t_/1/ => "_t_Iamheroyeah"
s/^_t_(.*)hero//1hero/ => "Iamheroyeah"
s/^_t_.*$/bar/ => "Iamheroyeah"
output = "Iamheroyeah"
input = "Heisnotagoodman"
s/^(.*)$/_t_/1/ => "_t_Heisnotagoodman"
s/^_t_(.*)hero//1hero/ => "_t_Heisnotagoodman"
s/^_t_.*$/bar/ => "bar"
output = "bar"
注:若是用java语言的正则表达式,就不用这么麻烦了,只用“?!”就可以了。