C#实现缺失Table、TR、TD标签自动注入与表格标准化
修复缺失Table/TR/TD标签并标准化表格的C#实现方案
核心思路
用Html Agility Pack(HAP)处理标签补全,它比正则更适配HTML解析场景;补全后遍历表格行,统计最大TD数量,再给不足的行前置空TD。
完整实现代码
using HtmlAgilityPack; using System; using System.Collections.Generic; using System.Linq; public class TableNormalizer { public static string FixAndNormalizeTable(string html) { var doc = new HtmlDocument(); // 开启HAP的标签修复配置 doc.OptionFixNestedTags = true; doc.OptionAutoCloseOnEnd = true; doc.OptionDefaultStreamEncoding = System.Text.Encoding.UTF8; doc.LoadHtml(html); var tables = doc.DocumentNode.SelectNodes("//table"); if (tables == null) return html; foreach (var table in tables) { // 补全TR和TD标签 FixTableTags(table, doc); // 统计每行有效TD数量,取最大值 var rows = table.SelectNodes(".//tr"); if (rows == null) continue; int maxTdCount = rows.Max(tr => tr.SelectNodes(".//td")?.Count(td => td.NodeType == HtmlNodeType.Element) ?? 0); // 给TD数量不足的行前置空TD foreach (var row in rows) { var tds = row.SelectNodes(".//td")?.Where(td => td.NodeType == HtmlNodeType.Element).ToList() ?? new List<HtmlNode>(); int missing = maxTdCount - tds.Count; if (missing > 0) { for (int i = 0; i < missing; i++) { var emptyTd = doc.CreateElement("td"); row.PrependChild(emptyTd); } } } } return doc.DocumentNode.OuterHtml; } private static void FixTableTags(HtmlNode table, HtmlDocument doc) { HtmlNode currentRow = null; HtmlNode currentCell = null; // 遍历table下所有节点,处理未闭合标签和无包裹文本 var nodesToProcess = new List<HtmlNode>(table.ChildNodes); foreach (var node in nodesToProcess) { if (node.NodeType == HtmlNodeType.Element) { switch (node.Name.ToLower()) { case "tr": // 闭合之前未结束的行 if (currentRow != null && !currentRow.Closed) { currentRow.InnerHtml += "</tr>"; } currentRow = node; currentCell = null; // 确保当前行有闭合标签 if (!currentRow.Closed) { currentRow.InnerHtml += "</tr>"; } break; case "td": currentCell = node; // 无行包裹时,自动创建TR if (currentRow == null) { currentRow = doc.CreateElement("tr"); table.AppendChild(currentRow); } // 确保当前单元格有闭合标签 if (!currentCell.Closed) { currentCell.InnerHtml += "</td>"; } break; default: // 遇到非表格元素,先闭合当前未结束的单元格和行 if (currentCell != null && !currentCell.Closed) { currentCell.InnerHtml += "</td>"; currentCell = null; } if (currentRow != null && !currentRow.Closed) { currentRow.InnerHtml += "</tr>"; currentRow = null; } break; } } else if (node.NodeType == HtmlNodeType.Text && !string.IsNullOrWhiteSpace(node.InnerText)) { // 游离文本自动用TD+TR包裹 if (currentCell == null) { currentRow = currentRow ?? doc.CreateElement("tr"); table.AppendChild(currentRow); currentCell = doc.CreateElement("td"); currentRow.AppendChild(currentCell); } currentCell.AppendChild(node.CloneNode(true)); } } // 处理最后未闭合的行和单元格 if (currentCell != null && !currentCell.Closed) { currentCell.InnerHtml += "</td>"; } if (currentRow != null && !currentRow.Closed) { currentRow.InnerHtml += "</tr>"; } } } // 使用示例 class Program { static void Main() { string brokenHtml = @"<table> <tr><td>行1列1<td>行1列2 行2列1 <tr>行2列2<td>行2列3 </table>"; string fixedHtml = TableNormalizer.FixAndNormalizeTable(brokenHtml); Console.WriteLine(fixedHtml); } }
关键说明
- 标签补全逻辑:
- 开启HAP的修复配置,让框架自动处理大部分标签嵌套问题;
- 手动遍历节点,处理游离文本、未闭合TR/TD的极端情况,确保所有内容都被正确包裹在TR和TD中。
- 标准化表格:
- 统计时过滤非元素节点(如注释、空白文本),避免TD数量统计错误;
- 不足的TD从行首插入,保证原有列的顺序不变。
- 失败原因分析:
- 正则无法处理HTML的嵌套、标签顺序混乱等复杂场景,容错性极差;
- 若之前用HAP未开启修复选项,或未过滤非元素节点,会导致标签补全不彻底、TD数量统计错误。
内容的提问来源于stack exchange,提问作者Thomas
相关产品推荐
相关产品推荐

