我想编写一个Perl程序,使$_
中的合理HTML5标记更好(“更有效” –我知道,听起来像“更怀孕”)。具体来说,我想尝试使用</p>
标记正确关闭段落,就在浏览器将其关闭的地方。它是将html转换为xhtml的一步。这有助于我对全文进行后续文本分析。
HTML5规范说
p
元素必须具有开始标签。 p
元素后紧跟着p
,address
,article
,aside
,blockquote
,dir
,div
,dl
,fieldset
,footer
,form
,h1
,h2
,h3
,h4
,h5
, h6
,header
,hr
,menu
,nav
,ol
,p
,pre
,section
或table
元素ul
元素。 问题:
a
。例如,<p>
将在<h1>HEADER</h1> Now is…
之前插入一个<p>
。我错了吗Now is…
。我现在需要向前搜索直到结束。从26个标记中,找出一个段落的开头很容易。 <p>
,还是需要对全栈机器进行编码(假设段落中的所有内容本身都是有效的XHTML)来检测封闭容器的结尾? 感谢@Palec,我现在了解到段落在HTML中是一个奇怪的概念。尝试这个:
<!DOCTYPE html>
<html>
<head>
<style>
p { color: blue; }
p:before { content:"[SP]"; }
p:after { content:"[EP]"; }
</style>
</head>
<body>
l0
<h1> h1 </h1>
l0
<p> para
<p> para </p>
l0
<p>para
<ol>
<li> l0 <p> para </li>
</ol>
l0
</body>
</html>
这表明并非所有文本都至少是一个段落。我确实将其与LaTeX概念混淆了……并认为默认情况下,“0级”处的内容是一个段落。它不是。
请您参考如下方法:
段落的三个概念
HTML 5具有两个独立的概念:p
元素和段落。我将此段称为结构性段。在现实世界中,我至少发现了另外两个相关概念:逻辑段落和印刷段落。
p
element 已清除。您知道的,您已经在规范中引用了它的描述。
(Structural) paragraph 对我来说有点奇怪。屏幕阅读器或其他任何工具都可以使用它。它的定义基本上说这是phrasing content的非空运行,不会被其他类型的内容打扰(不考虑a
,ins
,del
和map
)。
逻辑段落是我认为人类认为的段落。它是一个带有单一思想的文本单元。当另一个(可能是相关的)想法开始时,该段中断并且开始一个新的想法。它由一系列句子组成。
每个句子不仅可以具有其语言结构,而且可以包含格式。格式不限于HTML所称的措辞内容,而是我将至少添加多行预格式化的代码段,列表,数学公式(可能跨越多行,显示来自TeX的数学运算)以及可以在中间使用的任何其他内容句子之间或句子之间,同时又不中断思路。逻辑段落和其他两个概念之间的巨大区别可以在我的问题List or longer code snippet inside paragraph中看到。
印刷段落由行的顺序而不是句子组成,并且可以包含印刷系统可以处理的所有内容。我最初认为它与逻辑段落完全相同,但事实并非如此。
当想到tex时,我想到了。您可能从latex中知道了这一点,它只是TeX的一大堆定义,并且具有与段落相同的概念。内容一直缓冲到\par
(或内部翻译为\par
的空行)得到满足为止,然后将其刷新为单个段落的输出。看起来像一个(逻辑)段落在内部可以是几个段落,因为必须使用它来实现排版算法的一些更复杂的行为。从这个角度来看,它更像是一个结构段落。
您的问题的答案
h1
元素之后开始。但这不是p
元素。它不能使用p
选择器在CSS中设置样式,它不在文档的DOM树中等。在某些地方元素标记不在标记中,但仍会创建元素。对于那些可以省略其开始标记的元素,情况就是如此。这些是
html
,head
,body
,colgroup
和tbody
。 (至少tbody
过去在HTML 4中的行为不同,此行为来自XHTML。在HTML中,它不必存在。)p
元素不是这种情况。 <p>
(它不是有效的HTML 5),您将如何纠正它?一旦不正确,通常就无法承担任何责任。还省略了结束标记是而不是不正确!这个列表项不是一个真正的问题,所以进一步……option
(在select
内)。这会破坏您的深度跟踪。当命名的元素之一开始,遇到
</p>
关闭标签或遇到父元素的结尾时,该段落关闭。嗯当您仅在内部假设有效的XHTML时,您仍然需要对所有元素实施关闭规则,以便能够检测父元素的结尾……这并非易事。 HTML 5的HTML到XML序列化转换
在评论中,您说过将HTML 5转换为XHTML 5是您的用例。
不要使用正则表达式!
正则表达式并非旨在执行诸如解析HTML之类的复杂任务。您尝试的任何事情都只是一种启发。根本没有 True regular expressions cannot parse HTML,因为HTML并不是 regular language。让我们抛开一切,Perlre更加强大;权力越大,责任就越大,如果权力不对,就不要使用权力。关于这方面的问题,这是一个真正的艺术品,它带有一个 extremely famous answer。 Jeff Atwood撰写了 more on the topic,在开头引用了这个答案,并在本文的其余部分中解释了理解您的工具的重要性。
我认为实现此目标的文本级方法是不好的。 HTML通常被称为 tag soup,与Wikipedia所说的相反,我遇到了这个术语,指的是文本级方法来创建和修改它(即
document.write()
和
element.innerHTML
)。
顺便说一句,这是XHTML通过废除真正解决的一件事。在JavaScript中,您不能将
document.write()
与XHTML一起使用。如果可行,则说明您将HTML解析器与XHTML文档一起使用–将
Content-Type
HTTP header 与
application/xhtml+xml; charset=utf-8
一起使用,而不是
text/html
所使用的MIME类型。
使用DOM
Clean Solution™是 DOM。
我相信您应该实现 HTML解析器(或使用其他实现),获取DOM树,并将序列化器写入 XHTML。如果输入文档无效,则拒绝对其进行处理。或在程序中添加开关,以告诉它如何解决解析算法无法处理的某些错误。可能有很多方法。
我不确定如果您对规范的哪些部分不感兴趣,可以随意忽略。解析算法是标准化的,并且还指定了错误处理。您可以找到一种快捷方式,无需创建DOM树的一部分,而只需将输入的相应部分保留为未解析状态,但必须确保继续在输入的正确位置进行解析。这可能会变得凌乱,并且绝对容易出错。因此,我建议您不要这样做。
实际解决方案
实际上,似乎可以使用至少两个现有模块。
Mojolicious是包含 Mojo::DOM模块的Web框架。如果不需要DOM操作,而只需要解析和序列化,则可以使用基础 Mojo::DOM::HTML。 Mojo::DOM可以使用
my $dom = Mojo::DOM->new($html_markup);
解析HTML,可以将生成的DOM对象设置为通过
$dom->xml(1);
使用XML序列化,并且序列化可以作为
$xhtml_markup = "$dom";
或
$xhtml_markup = $dom->to_string();
返回。来自Mojo::DOM POD:“Mojo::DOM是具有CSS选择器支持的简约且轻松的HTML / XML DOM解析器。它甚至会尝试解释损坏的XML,因此您不应将其用于验证。”在
answer by amon中使用的示例。如果您已经使用Mojolicious,则可能要使用此解决方案,否则,安装整个大型框架对于这项工作来说是过大的选择。
HTML::HTML5::Parser和 HTML::HTML5::Writer模块可以分别用于HTML 5的解析和序列化。他们似乎只有几个依赖性。使用它们的漂亮代码可以在其作者 answer by tobyink中找到。对于尚未使用Mojolicious的用户来说,这应该是一个解决方案。