php - 如何在也使用strip_tags的php字符串中替换多个小于<的实例?
问题描述
我将以下字符串存储在包含 HTML 的数据库表中,在网页上呈现之前我需要删除(这是我无法控制的旧内容)。
<p>I am <30 years old and weight <12st</p>
当我使用strip_tags
它时,它只显示I am
.
我理解为什么 strip_tags 会这样做,所以我需要将 2 个实例替换<
为<
我找到了一个转换第一个实例而不是第二个实例的正则表达式,但我不知道如何修改它以替换所有实例。
/<([^>]*)(<|$)/
这导致I am currently <30 years old and less than
我在这里有一个演示https://eval.in/1117956
解决方案
尝试使用字符串函数解析 html 内容是一个坏主意,包括正则表达式函数(有很多主题可以解释这一点,请搜索它们)。html 太复杂了,无法做到这一点。
问题是您无法控制的 html 格式不正确。有两种可能的态度:
- 没有什么可做的:数据已损坏,因此信息一劳永逸地丢失,您无法检索已经消失的东西,仅此而已。这是一个完全可以接受的观点。可能您可以在某处找到相同数据的另一个来源,或者您可以选择打印格式不佳的 html。
- 你可以尝试修复。在这种情况下,您必须确保所有文档问题都受到限制并且可以解决(至少可以手动解决)。
代替直接字符串方法,您可以通过DOMDocument
. 即使 libxml 解析器不会给出比 更好的结果strip_tags
,它也会提供错误,您可以使用它来识别错误类型并在 html 字符串中找到有问题的位置。
使用您的字符串,libxml 解析器XML_ERR_NAME_REQUIRED
会在每个有问题的左尖括号上返回带有代码 68 的可恢复错误。使用 可以看到错误libxml_get_errors()
。
您的字符串示例:
$s = '<p>I am <30 years old and weight <12st</p>';
$libxmlErrorState = libxml_use_internal_errors(true);
function getLastErrorPos($code) {
$errors = array_filter(libxml_get_errors(), function ($e) use ($code) {
return $e->code === $code;
});
if ( !$errors )
return false;
$lastError = array_pop($errors);
return ['line' => $lastError->line - 1, 'column' => $lastError->column - 2 ];
}
define('XML_ERR_NAME_REQUIRED', 68); // xmlParseEntityRef: no name
$patternTemplate = '~(?:.*\R){%d}.{%d}\K<~A';
$dom = new DOMDocument;
$dom->loadHTML($s, LIBXML_HTML_NODEFDTD | LIBXML_HTML_NOIMPLIED);
while ( false !== $position = getLastErrorPos(XML_ERR_NAME_REQUIRED) ) {
libxml_clear_errors();
$pattern = vsprintf($patternTemplate, $position);
$s = preg_replace($pattern, '<', $s, 1);
$dom = new DOMDocument;
$dom->loadHTML($s, LIBXML_HTML_NODEFDTD | LIBXML_HTML_NOIMPLIED);
}
echo $dom->saveHTML();
libxml_clear_errors();
libxml_use_internal_errors($libxmlErrorState);
$patternTemplate
是一个格式化字符串(参见sprintf
php 手册),其中占位符%d
分别代表之前的行数和从行首开始的位置。(此处为 0 和 8)
模式细节:模式的目标是从字符串的开头到达尖括号位置。
~ # my favorite pattern delimiter
(?:
.* # all character until the end of the line
\R # the newline sequence
){0} # reach the desired line
.{8} # reach the desired column
\K # remove all on the left from the match result
< # the match result is only this character
~A # anchor the pattern at the start of the string
我使用类似技术的另一个相关问题:手动解析无效 XML