提问人:behroz 提问时间:9/3/2023 最后编辑:Simonbehroz 更新时间:9/3/2023 访问量:53
在 PHP 中将 html 标题转换为列表元素
Convert html headlines to list elements in PHP
问:
我正在学习语言。
我想显示文章的目录。将标题转换为列表并创建链接。
这是我的php代码。php
(h2,h3,h4,...)
$Post = '
<h2>Title 01</h2>
<h3>Title 01.01</h3>
<h3>Title 01.02</h3>
<h2>Title 02</h2>
<h3>Title 02.02</h3>
';
$c = 1;
$r = preg_replace_callback('~<h*([^>]*)>~i', function($res) use (&$c){
return '<li><a id="#id'.$c++.'">'.$res[1].'</a></li>';
}, $Post);
$Post = $r;
echo '<ul>';
echo $Post;
echo '</ul>';
输出如下所示,但上面的代码工作错误。
<ul>
<li><a id="#id1">2</a></li>Title 01<li><a id="#id2">/h2</a></li>
<li><a id="#id3">3</a></li>Title 01.01<li><a id="#id4">/h3</a></li>
<li><a id="#id5">3</a></li>Title 01.02<li><a id="#id6">/h3</a></li>
<li><a id="#id7">2</a></li>Title 02<li><a id="#id8">/h2</a></li>
<li><a id="#id9">3</a></li>Title 02.02<li><a id="#id10">/h3</a></li>
</ul>
我知道PHP代码写错了。但我想显示如下所示的输出。
<ul>
<li><a href="#id1">Title 01</a></li>
<li><a href="#id2">Title 01.01</a></li>
<li><a href="#id3">Title 01.02</a></li>
<li><a href="#id4">Title 02</a></li>
<li><a href="#id5">Title 02.02</a></li>
</ul>
答:
2赞
Simon
9/3/2023
#1
您的正则表达式非常复杂。
你可以用它来正确匹配你试图匹配的东西。<h.>(.*)</h.>
我将其添加到上面的代码片段中以显示您想要的结果:
$post = '
<h2>Title 01</h2>
<h3>Title 01.01</h3>
<h3>Title 01.02</h3>
<h2>Title 02</h2>
<h3>Title 02.02</h3>
';
$c = 1;
$list_elements = preg_replace_callback('~<h.>(.*)</h.>~i', function($res) use (&$c){
return '<li><a id="#id'.$c++.'">'.$res[1].'</a></li>';
}, $post);
echo '<ul>';
echo $list_elements;
echo '</ul>';
虽然,正如评论中建议的那样,您可能应该在这里使用解析器,如果这不仅仅是一个玩具示例。那么正则表达式几乎总是搬起石头砸自己的脚的可靠方法。
评论
0赞
HorusKol
9/3/2023
+1 比我开始的模式简单得多 - 但正如你所说,处理更复杂的标记将需要(更)复杂的模式
1赞
HorusKol
9/3/2023
#2
你的正则表达式对于你试图做的事情是错误的:
~<h*([^>]*)>~i
<h*
表示它将匹配一个尖括号,后跟零个或多个 h。这基本上意味着您的正则表达式匹配每个<>对之间的所有内容,(包括 </...>).
您可以这样做以从标题中提取标题:
~<h[1-6]>([^<]*)<\h[1-6]>~i
但是那些链接的需要以标题中的 ID 为目标,因此您需要这样做来提取它们:
~<h[1-6] id="([^"]*)">([^<]*)<\h[1-6]>~i
但是,如果你的标题上有其他属性怎么办?
~<h[1-6][^>]*(id="([^"*])"[^>]*)?>([^<]*)<\h[1-6]>~i
还是在标题内标记?
正则表达式不是解析 HTML 的好方法。它是一个强大的工具,可以将其用于此目的,但有更好的方法。
$doc = new DOMDocument();
$doc->loadHTML($post);
$xpath = new DOMXPath($doc);
$headings = $xpath->query('html/body//*[self::h1 or self::h2 or self::h3]');
$nav = $xpath->query('html/body//nav/ul');
foreach ($headings as $heading) {
$link = $doc->createElement('a');
$link->setAttribute('href', '#' . $heading->getAttribute('id'));
$link->textContent = $heading->textContent;
$nav->appendChild(
$doc->createElement('li')
->appendChild($link)
);
}
我假设标题中没有标记,但如有必要,只需进行一些更改即可复制内部标记。
评论