miketwais

work up

php正则逆向引用与子模式分析

先看一个例子:

<?php
$string = 'April 15, 2003';
$pattern = '/(\w+) (\d+), (\d+)/i';
$replacement = '${1}1,$3';
echo preg_replace($pattern, $replacement, $string);
?>
View Code

例子的结果是:April1,2003

 

函数preg_replace ( mixed pattern, mixed replacement, mixed subject [, int limit])

在 subject 中搜索 pattern 模式的匹配项并替换为 replacement。如果指定了 limit,则仅替换 limit 个匹配,如果省略 limit 或者其值为 -1,则所有的匹配项都会被替换。
replacement可以包含\\n形式或$n形式的逆向引用,n可以为0到99,\\n表示匹配pattern第n个子模式的文本,\\0表示匹配整个pattern的文本。

所谓“子模式”就是:$pattern参数中被圆括号括起来的正则表达式(pattern即为模式)。

 

对上面例子中的 $replacement = '${1}1,$3';  

因为当在替换模式下工作并且后向引用后面紧跟着需要是另外一个数字, 不能使用\\1这样的语法来描述后向引用。\\11将会使preg_replace() 不能理解你希望的是一个\\1后向引用紧跟一个原文1,还是 一个\\11后向引用后面不跟任何东西。 这种情况下解决方案是使用\${1}1。 这创建了一个独立的$1后向引用, 一个独立的原文1

 

再看一个例子:

<?php 
$string = "Is is the cost of of gasoline going up up"; 
$pattern = "/\b([a-z]+) \\1\b/i"; //这里的\\1不能使用\$1或$1 
$str = preg_replace($pattern, "\\1", $string); //这里的\\1可以使用\$1或$1,引用第一个子匹配 
echo $str; 
?> 
View Code

结果为:Is the cost of gasoline going up    去掉了重复的内容。

例中的子表达式就是圆括号内的项。\b匹配单词的开始或结束。+匹配重复一次或更多次。 
该子表达式匹配的是一个或多个字母字符的单词,即由'[a-z]+'匹配的。 
该正则表达式的第二部分是对前面所捕获的子匹配的引用,也就是由附加表达式所匹配的第二次出现的单词,用'\\1'来引用第一个子匹配,第一个\是转义符。 
i是正则表达式中的修正符。i:忽略大小写。 

 

扩展:一个常见的面试题

在file.txt中按行存放着这样的一些数字

0013223544456
013423545456
1372-35--45456
132245-44556
13723-584456
1392-3544-456
132-255444-56
0132-275444-56

希望对其进行处理:除去首位的0,除去字符串中包含的-符号,并且将手机号码处理成132****456格式,处理后保存在newfile.txt文件中。

我的方法是:

<?php
$fp = fopen("file.txt", "r");
$newf = fopen("newfile.txt", "w"); 
while(! feof($fp)) 
{ 
$fgets = fgets($fp);
$fgets = preg_replace('/^0*|\D/', '', $fgets);
$pattern = "/(1\d{1,2})\d\d(\d{0,3})/";
$replacement = "\$1****\$3";
$fgets = preg_replace($pattern, $replacement, $fgets);
$text = $fgets."\r\n";
fwrite($newf, $text);

} 
fclose($fp); 
fclose($newf);
?>
View Code

 

newfile.txt中最总结果是:

132****456
134****456
137****456
132****556
137****456
139****456
132****456
132****456

 



posted @ 2015-05-08 00:30  MasonZhang  阅读(848)  评论(0编辑  收藏  举报