如何在preg_replace表达式中包含新行

时间:2012-02-05 00:03:27

标签: php regex preg-replace

我需要处理格式不正确的XML文件。

我希望在某些代码的内容中包含<![CDATA[ ... ]]>。我做了这样的事情:

$pattern = "/<$tagname?>(.*)?<\/$tagname>/"; 
$replacement = "<$tagname><![CDATA[$1]]></$tagname>";

$xml = file_get_contents($inputFilename);
preg_match($pattern, $xml, $match);
echo "\nFirst Ocurrence: " . $match[0]; 

$modifiedXml = preg_replace($pattern, $replacement, $xml);
preg_match($pattern, $modifiedXml, $match);

echo "\nFirst Ocurrence Modified: " . $match[0]; 

它运行良好,但是当我的XML节点有新行时,例如:

<node> foo
bar
</node>

它不起作用。我已经读过我必须放/s,但我不知道我该把它放在我的正则表达式中。

4 个答案:

答案 0 :(得分:1)

I don't have any idea where do I have to put it in my regex.

下面

$pattern = "/<$tagname?>(.*)?<\/$tagname>/s";

ps :. (点)捕获除新行之外的每个符号。 regexp修饰符s'告诉'捕获新行。

答案 1 :(得分:0)

从它的外观来看,你可以做的一件事是替换:

(.*)?

由:

((.|\s)*)?

当然这个问号很无用(在你的样本中也是如此),所以你可以把它改成:

((\s|.)*)

编辑:我想补充一点,我不认为这是一个简洁的解决方案,但是你的起始代码只需要很少的改动。

另一方面,一般来说,这个正则表达式在xml方面存在一些问题。如果文档中只有一个“tagname”-tag,则意识到它只能正常工作。

答案 2 :(得分:0)

首先,(.*)?不正确。它意味着“零个或多个任何字符,零次或一次”,这没有任何意义。你显然是(.*?),意思是“零或多个任何字符,非贪婪。”

它不匹配换行符的原因是(如@Cheery所解释的)因为这是正常的默认行为。如果您希望点匹配任何包括换行符,则必须指定单行模式(也称为 DOTALL模式)。在PHP中,您通常通过将/s标志添加到正则表达式的末尾(例如'/(.*?)/s')或在开头或正则表达式中插入内联修饰符(?s)来实现这一点(例如{{ 1}})。

还有其他有效的技巧。例如,在没有单行/ DOTALL模式的JavaScript中,大多数正则表达式作者使用'/(?s)(.*?)/',意思是“任何空白字符或任何不是空格的字符” - 换句话说,任何字符。

通常你甚至不需要担心它。例如,在像您这样的情况下,您可能知道您匹配的对之间没有其他标记,因此您可以使用[\s\S]匹配除[^<]之外的任何字符,因为 包含换行符。 (但如果XML格式错误,那么可能不是一种选择。)

使用的是<,这是另一个答案中的建议。正如在this answer中非常明确地解释的那样,由于(.|\s).匹配的字符集重叠,这个看似无辜的正则表达式很容易使正则表达式引擎减速到虚拟停止状态。 p>

我经常看到的另一个“显而易见”的方法是\s,但这也不安全。当我们说点与换行符不匹配时,这不仅仅意味着换行符((.|\n)\n)。根据正则表达式的风格,编译时配置和运行时系统设置,它还可以包括回车(U+000A\r),换页(U+000D\f)和其他几个字符(ref)。 U+000C的效率也明显低于其他选项,但可能不像(.|\n)那样灾难性。

答案 3 :(得分:0)

$pattern = "/<$tagname>([^\\0]*)?<\/$tagname>/";