正则表达式将一个大的html表拆分为多个5行的表

问题描述 投票:-1回答:1

我正在尝试使用regExps进行整理,所以我在路上遇到了一个问题:问题是我有一些带有纯文本和一个表的随机HTML文件。文本可以在表格之前和之后,表格不包括<thead><tbody><tfoot> rowspan等。因此,我需要将此表拆分为多个表,每个表包含5行,最后一行包含5个或更少,并在每个表中重复原始表的第一个字符串。例如:

<table>
  <tr>
   <td>A</td><td>B</td>
  </tr>
  <tr>
   <td>A1</td><td>B1</td>
  </tr>
  <tr>
   <td>C</td><td>D</td>
  </tr>
  <tr>
   <td>E</td><td>F</td>
  </tr>
  <tr>
   <td>E1</td><td>F1</td>
  </tr>
  <tr>
   <td>E2</td><td>F2</td>
  </tr>
  <tr>
   <td>E3</td><td>F3</td>
  </tr>
  <tr>
   <td>E4</td><td>F4</td>
  </tr>
</table>

应该成为:

<table>
  <tr>
   <td>A</td><td>B</td><--!!!(not needed to be in code)-->
  </tr>
  <tr>
   <td>A1</td><td>B1</td>
  </tr>
  <tr>
   <td>C</td><td>D</td>
  </tr>
  <tr>
   <td>E</td><td>F</td>
  </tr>
  <tr>
   <td>E1</td><td>F1</td>
  </tr>
</table>
<table>
  <tr>
   <td>A</td><td>B</td><--!!!(not needed to be in code)-->
  </tr>
  <tr>
   <td>E2</td><td>F2</td>
  </tr>
  <tr>
   <td>E3</td><td>F3</td>
  </tr>
  <tr>
   <td>E4</td><td>F4</td>
  </tr>
</table>

我需要在PHP中使用PCRE完成这些工作,包括大量的模板和更改。所以我在实现方面遇到了问题。现在我可以找到第一行像这个<table>\s*?(<tr>(?:\s|.)*?<\/tr>)和4个逐行(<tr>(?:\s|.)*?<\/tr>\s*){1,4}但我不知道如何找到第二个模板的所有出现所以我可以在以后使用它们如何停止搜索如果有是</table>表结束标记。所以请帮忙

编辑

问题已经回答,所以它的下一级添加原始表标签<thead><tbody><tfoot>。在输出表中,应该重建原始表的结构,所以我的意思是如果原始表的第一行是<thead>标记的一部分,它应该在<thead>中是所有输出表。

php html regex html-table pcre
1个回答
0
投票

您可以通过执行循环来实现此目的,其中每次迭代将使用preg_replace添加下一个“表中断”(但最后请参见免责声明)。建议的正则表达式将找到以下组:

  • <table>标签的最后一次出现以及跟随它的第一行,或者,如果有thead和/或tbody标签,直到关闭的</thead>标签,包括开头的<tbody>标签(如果有的话)。
  • 接下来的4个行。必须有4个。

然后它还展望未来,确保至少还有一行存在。

使用该信息,可以将单个“表中断”注入HTML字符串。

如果表有一个tfooter部分(然后也应该在表的每个分区中重复),我们将不会有这些信息,因为它发生在输入的最后。因此,在循环开始之前,需要单独解析以提取页脚。

这是假设输入在变量$html中的代码:

// Extract the footer part (if there is one) and closing table tag
preg_match("#(\s*(</tbody|<tfooter).*?)?</table>#s", $html, $tableEnd);
$tableEnd = $tableEnd[0];

// Add a table break in each iteration as long as the last partition has more than 4 rows:
while (true) {
    $res = preg_replace("#(<table(?!.*<table).*?/tr>(?:.*?/thead>)?(?:.*?<tbody>)?)((?:.*?/tr>(?=\s*<tr)){4})#s", 
                        "$1$2$tableEnd\n$1", $html);
    if (strlen($res) === strlen($html)) break;
    $html = $res;
}

echo $res;

看它在eval.in上运行。

Explanation of the main regex

以下是主要正则表达式中的一些亮点:

  • #:我用它作为正则表达式分隔符而不是/,以避免在正则表达式本身内逃避/。如果你需要使用/作为分隔符然后将每个/转义为\\/:一个反斜杠用于正则表达式,另一个用于在字符串文字的上下文中转义反斜杠。
  • (?!.*<table):确保在我们即将匹配的标签之后没有其他<table>标签。这是一个负面的展望。
  • ((?:.*?/tr>(?=\s*<tr)){4}):抓住4行,并且正向前看((?= ))要求每一行紧跟另一行。 (?: )模式不构成捕获组,但外括号确实创建了一个。

The replacement

如果替换将再次注入2个被捕获的组(即$1$2),那么什么都不会改变。额外的$tableEnd\n$1将关闭表(使用页脚)并通过重用第一个捕获组来启动下一个。这将包含包含第一行和/或表头的开头<table>标记。

Disclaimer

虽然上述内容可能在许多情况下都有效,但很有可能打破它,因为正则表达式不是解析/解释HTML的理想方法。你应该真的使用DOM api,PHP有一个:DOMDocument

© www.soinside.com 2019 - 2024. All rights reserved.