我真的很想写一个正则表达式解决方案。但是我之前有一些很好和复杂的解决方案。所以,这是一个非正则表达式解决方案。
简短说明:主要问题是保留HTML标签。如果去除HTML标签,我们可以很容易地搜索文本。所以:剥离这些!我们可以很容易地在剥离的内容中搜索,并生成我们想要剪切的子字符串。然后,尝试从 HTML 中删除此子字符串,同时保留标记。
优势:
- 搜索很容易,独立于HTML,如果需要,您也可以使用正则表达式进行搜索
- 需求是可扩展的:您可以轻松添加完整的多字节支持,对实体和空格折叠的支持等
- 相对较快(有可能,直接正则表达式可以更快)
- 不接触原始HTML,并适应其他标记语言
此方案的静态实用程序类:
class HtmlExtractUtil
{
const FAKE_MARKUP = '<>';
const MARKUP_PATTERN = '#<[^>]+>#u';
static public function extractBetween($html, $startTextToFind, $endTextToFind)
{
$strippedHtml = preg_replace(self::MARKUP_PATTERN, '', $html);
$startPos = strpos($strippedHtml, $startTextToFind);
$lastPos = strrpos($strippedHtml, $endTextToFind);
if ($startPos === false || $lastPos === false) {
return "";
}
$endPos = $lastPos + strlen($endTextToFind);
if ($endPos <= $startPos) {
return "";
}
return self::extractSubstring($html, $startPos, $endPos);
}
static public function extractSubstring($html, $startPos, $endPos)
{
preg_match_all(self::MARKUP_PATTERN, $html, $matches, PREG_OFFSET_CAPTURE);
$start = -1;
$end = -1;
$previousEnd = 0;
$stripPos = 0;
$matchArray = $matches[0];
$matchArray[] = [self::FAKE_MARKUP, strlen($html)];
foreach ($matchArray as $match) {
$diff = $previousEnd - $stripPos;
$textLength = $match[1] - $previousEnd;
if ($start == (-1)) {
if ($startPos >= $stripPos && $startPos < $stripPos + $textLength) {
$start = $startPos + $diff;
}
}
if ($end == (-1)) {
if ($endPos > $stripPos && $endPos <= $stripPos + $textLength) {
$end = $endPos + $diff;
break;
}
}
$tagLength = strlen($match[0]);
$previousEnd = $match[1] + $tagLength;
$stripPos += $textLength;
}
if ($start == (-1)) {
return "";
} elseif ($end == (-1)) {
return substr($html, $start);
} else {
return substr($html, $start, $end - $start);
}
}
}
用法:
$html = '
<html>
<body>
<p>Any string before</p>
<p>Hello <em>進撃の巨人</em>!</p>
random code
random code
<p>Lorem <span>ipsum<span>.</p>
<p>Any string after</p>
</body>
</html>
';
$startTextToFind = 'Hello 進撃の巨人!';
$endTextToFind = 'Lorem ipsum.';
$extractedText = HtmlExtractUtil::extractBetween($html, $startTextToFind, $endTextToFind);
header("Content-type: text/plain; charset=utf-8");
echo $extractedText . "\n";