You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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);
    }
}

关键说明

  1. 标签补全逻辑:
    • 开启HAP的修复配置,让框架自动处理大部分标签嵌套问题;
    • 手动遍历节点,处理游离文本、未闭合TR/TD的极端情况,确保所有内容都被正确包裹在TR和TD中。
  2. 标准化表格:
    • 统计时过滤非元素节点(如注释、空白文本),避免TD数量统计错误;
    • 不足的TD从行首插入,保证原有列的顺序不变。
  3. 失败原因分析:
    • 正则无法处理HTML的嵌套、标签顺序混乱等复杂场景,容错性极差;
    • 若之前用HAP未开启修复选项,或未过滤非元素节点,会导致标签补全不彻底、TD数量统计错误。

内容的提问来源于stack exchange,提问作者Thomas

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.26 14:41:10