preg_replace 多行匹配但保留新行
preg_replace multiline match but preserve new lines
我需要一个从 HTML 文件中修剪 PHP 的衬纸。诀窍是我还需要它来保留之前由 PHP 行占用的换行符。
php -r "echo preg_replace('/<\\?.*(\\?>|$)/Us','', file_get_contents($argv[1]));" -- "./index.php"
这个"works"但是不保留新行,例如:
<html><?php test(); ?>
<head>
<?php test();
?>
</head>
<body>
</body>
<html>
解析为:
<html>
<head>
</head>
<body>
</body>
<html>
但我需要它来解决:
<html>
<head>
</head>
<body>
</body>
<html>
也许我正在使用锤子来拧螺丝,但我想做的是删除 PHP 代码,运行 通过 htmlhint 得到结果,并使报告的行号与文件中的行。
如果有更好的解决方案,我很想听听。最终目标是用它们各自的 linters 对混合了 PHP、Javascript 和 HTML 的文件进行 lint。
简介
正则表达式绝对不是这个问题的最佳答案,但由于您正在寻找正则表达式形式的答案,这里有!
注意:如果注释或字符串包含 <?
.
,这将中断
代码
(?:\G(?!\A)|\h*(?=<\?))(.*(?=(?:(?!<\?)[\s\S])*?(?<=\?>)))
结果
输入
<html><?php test(); ?>
<head>
<?php test();
?>
</head>
<body>
</body>
<html>
输出
<html>
<head>
</head>
<body>
</body>
<html>
说明
(?:\G(?!\A)|\h*(?=<\?))
匹配以下任一选项
\G(?!\A)
\G
断言位置在上一个匹配的末尾或第一个匹配的字符串的开头
(?!\A)
否定前瞻断言后面的内容不是字符串的开头(这基本上使 \G
仅匹配上一个匹配项的结尾)
\h*(?=<\?)
匹配以下
\h*
匹配任意数量的水平空格(用于清理<?
之前的空格
(?=<\?)
正面前瞻确保以下匹配
<
按字面意思匹配小于号<
\?
按字面意思匹配问号字符?
(.*(?=(?:(?!<\?)[\s\S])*?(?<=\?>)))
将以下内容捕获到捕获组 1
.*
匹配任意字符(行终止符除外)任意次数
(?=(?:(?!<\?)[\s\S])*?(?<=\?>))
正向前瞻确保后续匹配
(?:(?!<\?)[\s\S])*?
匹配以下任意次数,但越少越好
(?!<\?)
负前瞻确保后面的内容不匹配
<
按字面意思匹配小于号<
\?
按字面意思匹配问号字符?
[\s\S]
匹配任意字符
(?<=\?>)
负向回顾确保前面的内容与以下内容匹配
\?
按字面意思匹配问号字符?
>
按字面意思匹配大于号>
Ok 一行使用分词器(里面丑陋的东西):
php -r 'echo array_reduce(token_get_all(file_get_contents($argv[1])),function($c,$i){return $i[0]==321?$c.$i[1]:$c.str_repeat("\n",@count_chars($i.$i[1])[10]);});'
tokenizer 的优势:即使像 "abc <?php echo '?>'; ?> def"
这样的字符串也能被正确解析。
321 是常量 T_INLINE_HTML
的值(所有不在 php 标记之间的值)。
10 是换行符 (LF) 的 ASCII 码。 (默认情况下,count_chars
returns 以ASCII码为键,出现次数为值的关联数组).
丑陋的是 $i.$i[1]
将数组与字符串或字符串与未定义的内容连接起来。 @
避免警告和通知。不管怎样,这个技巧避免了测试并且换行符的数量被保留了下来。 (看什么returnstoken_get_all
就明白问题了)
或 DOMDocument
:
php -r '$d=DOMDocument::loadHTMLFile($argv[1],8196);foreach((new DOMXPath($d))->query("//processing-instruction()")as$p)$p->parentNode->replaceChild($d->createTextNode(preg_replace("~\S+~","",$p->nodeValue)),$p);echo$d->saveHTML();'
我需要一个从 HTML 文件中修剪 PHP 的衬纸。诀窍是我还需要它来保留之前由 PHP 行占用的换行符。
php -r "echo preg_replace('/<\\?.*(\\?>|$)/Us','', file_get_contents($argv[1]));" -- "./index.php"
这个"works"但是不保留新行,例如:
<html><?php test(); ?>
<head>
<?php test();
?>
</head>
<body>
</body>
<html>
解析为:
<html>
<head>
</head>
<body>
</body>
<html>
但我需要它来解决:
<html>
<head>
</head>
<body>
</body>
<html>
也许我正在使用锤子来拧螺丝,但我想做的是删除 PHP 代码,运行 通过 htmlhint 得到结果,并使报告的行号与文件中的行。
如果有更好的解决方案,我很想听听。最终目标是用它们各自的 linters 对混合了 PHP、Javascript 和 HTML 的文件进行 lint。
简介
正则表达式绝对不是这个问题的最佳答案,但由于您正在寻找正则表达式形式的答案,这里有!
注意:如果注释或字符串包含 <?
.
代码
(?:\G(?!\A)|\h*(?=<\?))(.*(?=(?:(?!<\?)[\s\S])*?(?<=\?>)))
结果
输入
<html><?php test(); ?>
<head>
<?php test();
?>
</head>
<body>
</body>
<html>
输出
<html>
<head>
</head>
<body>
</body>
<html>
说明
(?:\G(?!\A)|\h*(?=<\?))
匹配以下任一选项\G(?!\A)
\G
断言位置在上一个匹配的末尾或第一个匹配的字符串的开头(?!\A)
否定前瞻断言后面的内容不是字符串的开头(这基本上使\G
仅匹配上一个匹配项的结尾)
\h*(?=<\?)
匹配以下\h*
匹配任意数量的水平空格(用于清理<?
之前的空格
(?=<\?)
正面前瞻确保以下匹配<
按字面意思匹配小于号<
\?
按字面意思匹配问号字符?
(.*(?=(?:(?!<\?)[\s\S])*?(?<=\?>)))
将以下内容捕获到捕获组 1.*
匹配任意字符(行终止符除外)任意次数(?=(?:(?!<\?)[\s\S])*?(?<=\?>))
正向前瞻确保后续匹配(?:(?!<\?)[\s\S])*?
匹配以下任意次数,但越少越好(?!<\?)
负前瞻确保后面的内容不匹配<
按字面意思匹配小于号<
\?
按字面意思匹配问号字符?
[\s\S]
匹配任意字符
(?<=\?>)
负向回顾确保前面的内容与以下内容匹配\?
按字面意思匹配问号字符?
>
按字面意思匹配大于号>
Ok 一行使用分词器(里面丑陋的东西):
php -r 'echo array_reduce(token_get_all(file_get_contents($argv[1])),function($c,$i){return $i[0]==321?$c.$i[1]:$c.str_repeat("\n",@count_chars($i.$i[1])[10]);});'
tokenizer 的优势:即使像 "abc <?php echo '?>'; ?> def"
这样的字符串也能被正确解析。
321 是常量 T_INLINE_HTML
的值(所有不在 php 标记之间的值)。
10 是换行符 (LF) 的 ASCII 码。 (默认情况下,count_chars
returns 以ASCII码为键,出现次数为值的关联数组).
丑陋的是 $i.$i[1]
将数组与字符串或字符串与未定义的内容连接起来。 @
避免警告和通知。不管怎样,这个技巧避免了测试并且换行符的数量被保留了下来。 (看什么returnstoken_get_all
就明白问题了)
或 DOMDocument
:
php -r '$d=DOMDocument::loadHTMLFile($argv[1],8196);foreach((new DOMXPath($d))->query("//processing-instruction()")as$p)$p->parentNode->replaceChild($d->createTextNode(preg_replace("~\S+~","",$p->nodeValue)),$p);echo$d->saveHTML();'