将 html 字符串拆分为 N 部分

2024-03-06

有没有人有一个使用 C# 拆分 html 字符串(来自tinymce 编辑器)并将其拆分为 N 个部分的示例?

我需要均匀地分割字符串而不分割单词。

我正在考虑只拆分 html 并使用 HtmlAgilityPack 来尝试修复损坏的标签。虽然我不确定如何找到分割点,但理想情况下它应该基于文本而不是 html。

有人对如何解决这个问题有任何想法吗?

UPDATE

根据要求,这里是输入和所需输出的示例。

INPUT:

<p><strong>Lorem ipsum dolor sit amet, <em>consectetur adipiscing</em></strong> elit.</p>

输出(分成 3 列时):

Part1: <p><strong>Lorem ipsum dolor</strong></p>
Part2: <p><strong>sit amet, <em>consectetur</em></strong></p>
Part3: <p><strong><em>adipiscing</em></strong> elit.</p>

更新2:

我刚刚尝试过 Tidy HTML,它似乎可以很好地修复损坏的标签,所以如果我能找到一种方法来定位分割品脱,这可能是一个不错的选择?

UPDATE 3

使用与此类似的方法在 .NET C# 中截断整个单词的字符串 https://stackoverflow.com/questions/1613896/truncate-string-on-whole-words-in-net-c,我现在已经成功获得了组成每个部分的纯文本单词列表。那么,假设使用 Tidy HTML,我有一个有效的 html XML 结构,并且考虑到这个单词列表,有人知道现在分割它的最佳方法是什么吗?

UPDATE 4

任何人都可以看到使用正则表达式通过以下方式查找 HTML 索引的问题:

给定纯文本字符串“sit amet, consectetur”,用正则表达式“(\s|)*”替换所有空格,理论上找到具有空格和/或的任意组合的字符串标签

然后我可以使用 Tidy HTML 来修复损坏的 html 标签吗?

非常感谢

Matt


建议的解决方案

伙计,这是一个curse我的!显然,我无法在不花费最多(包括)的情况下解决问题不合理花费的时间。

我想到了这一点。我考虑过 HTML Tidy,也许它会起作用,但我无法理解它。

所以,我写了自己的解决方案。

我测试了这个your输入以及我自己整理的其他一些输入。看起来效果很好。当然其中存在漏洞,但它可能为您提供一个起点。

无论如何,我的方法是这样的:

  1. 使用一个类封装 HTML 文档中单个单词的概念,该类包含有关该单词在 HTML 文档层次结构中的位置的信息,直到给定的“顶部”。这我已经在HtmlWord下面的课。
  2. 创建一个能够编写由上述 HTML 单词组成的单行的类,以便在适当的位置添加开始元素和结束元素标记。这我已经在HtmlLine下面的课。
  3. 编写一些扩展方法,使这些类可以直接从HtmlAgilityPack.HtmlNode目的。这些我已经在HtmlHelper下面的课。

我做这一切是疯了吗?可能是。但是,你知道,如果你无法弄清楚任何other方式,你可以尝试一下。

以下是它如何处理示例输入:

var document = new HtmlDocument();
document.LoadHtml("<p><strong>Lorem ipsum dolor sit amet, <em>consectetur adipiscing</em></strong> elit.</p>");

var nodeToSplit = document.DocumentNode.SelectSingleNode("p");
var lines = nodeToSplit.SplitIntoLines(3);

foreach (var line in lines)
    Console.WriteLine(line.ToString());

Output:

<p><strong>Lorem ipsum dolor </strong></p>
<p><strong>sit amet, <em>consectetur </em></strong></p>
<p><strong><em>adipiscing </em></strong>elit. </p>

现在是代码:

HtmlWord类

using System;
using System.Collections.Generic;
using System.Linq;

using HtmlAgilityPack;

public class HtmlWord {
    public string Text { get; private set; }
    public HtmlNode[] NodeStack { get; private set; }

    // convenience property to display list of ancestors cleanly
    // (for ease of debugging)
    public string NodeList {
        get { return string.Join(", ", NodeStack.Select(n => n.Name).ToArray()); }
    }

    internal HtmlWord(string text, HtmlNode node, HtmlNode top) {
        Text = text;
        NodeStack = GetNodeStack(node, top);
    }

    private static HtmlNode[] GetNodeStack(HtmlNode node, HtmlNode top) {
        var nodes = new Stack<HtmlNode>();

        while (node != null && !node.Equals(top)) {
            nodes.Push(node);
            node = node.ParentNode;
        };

        return nodes.ToArray();
    }
}

HtmlLine类

using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Xml;

using HtmlAgilityPack;

[Flags()]
public enum NodeChange {
    None = 0,
    Dropped = 1,
    Added = 2
}

public class HtmlLine {
    private List<HtmlWord> _words;
    public IList<HtmlWord> Words {
        get { return _words.AsReadOnly(); }
    }

    public int WordCount {
        get { return _words.Count; }
    }

    public HtmlLine(IEnumerable<HtmlWord> words) {
        _words = new List<HtmlWord>(words);
    }

    private static NodeChange CompareNodeStacks(HtmlWord x, HtmlWord y, out HtmlNode[] droppedNodes, out HtmlNode[] addedNodes) {
        var droppedList = new List<HtmlNode>();
        var addedList = new List<HtmlNode>();

        // traverse x's NodeStack backwards to see which nodes
        // do not include y (and are therefore "finished")
        foreach (var node in x.NodeStack.Reverse()) {
            if (!Array.Exists(y.NodeStack, n => n.Equals(node)))
                droppedList.Add(node);
        }

        // traverse y's NodeStack forwards to see which nodes
        // do not include x (and are therefore "new")
        foreach (var node in y.NodeStack) {
            if (!Array.Exists(x.NodeStack, n => n.Equals(node)))
                addedList.Add(node);
        }

        droppedNodes = droppedList.ToArray();
        addedNodes = addedList.ToArray();

        NodeChange change = NodeChange.None;
        if (droppedNodes.Length > 0)
            change &= NodeChange.Dropped;
        if (addedNodes.Length > 0)
            change &= NodeChange.Added;

        // could maybe use this in some later revision?
        // not worth the effort right now...
        return change;
    }

    public override string ToString() {
        if (WordCount < 1)
            return string.Empty;

        var lineBuilder = new StringBuilder();

        using (var lineWriter = new StringWriter(lineBuilder))
        using (var xmlWriter = new XmlTextWriter(lineWriter)) {
            var firstWord = _words[0];
            foreach (var node in firstWord.NodeStack) {
                xmlWriter.WriteStartElement(node.Name);
                foreach (var attr in node.Attributes)
                    xmlWriter.WriteAttributeString(attr.Name, attr.Value);
            }
            xmlWriter.WriteString(firstWord.Text + " ");

            for (int i = 1; i < WordCount; ++i) {
                var previousWord = _words[i - 1];
                var word = _words[i];

                HtmlNode[] droppedNodes;
                HtmlNode[] addedNodes;

                CompareNodeStacks(
                    previousWord,
                    word,
                    out droppedNodes,
                    out addedNodes
                );

                foreach (var dropped in droppedNodes)
                    xmlWriter.WriteEndElement();
                foreach (var added in addedNodes) {
                    xmlWriter.WriteStartElement(added.Name);
                    foreach (var attr in added.Attributes)
                        xmlWriter.WriteAttributeString(attr.Name, attr.Value);
                }

                xmlWriter.WriteString(word.Text + " ");

                if (i == _words.Count - 1) {
                    foreach (var node in word.NodeStack)
                        xmlWriter.WriteEndElement();
                }
            }
        }

        return lineBuilder.ToString();
    }
}

HtmlHelper静态类

using System;
using System.Collections.Generic;
using System.Linq;

using HtmlAgilityPack;

public static class HtmlHelper {
    public static IList<HtmlLine> SplitIntoLines(this HtmlNode node, int wordsPerLine) {
        var lines = new List<HtmlLine>();

        var words = node.GetWords(node.ParentNode);

        for (int i = 0; i < words.Count; i += wordsPerLine) {
            lines.Add(new HtmlLine(words.Skip(i).Take(wordsPerLine)));
        }

        return lines.AsReadOnly();
    }

    public static IList<HtmlWord> GetWords(this HtmlNode node, HtmlNode top) {
        var words = new List<HtmlWord>();

        if (node.HasChildNodes) {
            foreach (var child in node.ChildNodes)
                words.AddRange(child.GetWords(top));
        } else {
            var textNode = node as HtmlTextNode;
            if (textNode != null && !string.IsNullOrEmpty(textNode.Text)) {
                string[] singleWords = textNode.Text.Split(
                    new string[] {" "},
                    StringSplitOptions.RemoveEmptyEntries
                );
                words.AddRange(
                    singleWords
                        .Select(w => new HtmlWord(w, node.ParentNode, top)
                    )
                );
            }
        }

        return words.AsReadOnly();
    }
}

结论

重申一下:这是一个拼凑在一起的解决方案;我确信它有问题。我将其仅作为您考虑的起点 - 再次强调,如果您无法通过其他方式获得您想要的行为。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

将 html 字符串拆分为 N 部分 的相关文章

  • 如何将包含 5000 条记录的 Excel 文件插入到 documentDB 中?

    我有一个 Excel 文件 最初约有 200 行 我能够将 Excel 文件转换为数据表 并且所有内容都正确插入到 documentdb 中 Excel 文件现在有 5000 行 在插入 30 40 条记录后不会插入 其余所有行不会插入到
  • 如何从RichTextBox中获取显示的文本?

    如何获得显示的RichTextBox 中的文本 我的意思是 如果 RichTextBox 滚动到末尾 我只想接收那些对我来说可见的行 P S 获得第一个显示的字符串就足够了 您想使用 RichTextBox GetCharIndexFrom
  • 密码验证 PHP 正则表达式

    我是正则表达式的新手 我需要使用 php 验证密码 并使用正则表达式执行以下密码策略 密码 必须至少有 8 个字符 必须有2个号码 允许的符号有 我已经尝试过以下方法 d A Za z 0 9A Za z 以下完全符合您的要求 d d 0
  • 将图像文件从网址复制到本地文件夹?

    我有该图像的网址 例如 http testsite com web abc jpg http testsite com web abc jpg 我想将该 URL 复制到 c images 中的本地文件夹中 而且当我将该文件复制到文件夹中时
  • 键盘加速器在 UWP 应用中停止工作

    我正在尝试将键盘加速器添加到 UWP 应用程序中的 CommandBar 菜单项 当应用程序启动时 这工作正常 但在我第一次打开溢出菜单后 加速器停止工作 这似乎不会发生在主要命令 菜单之外 上 只有溢出菜单内的辅助命令才会发生 此外 单击
  • 在 MATLAB 中创建共享库

    一位研究人员在 MATLAB 中创建了一个小型仿真 我们希望其他人也能使用它 我的计划是进行模拟 清理一些东西并将其变成一组函数 然后我打算将其编译成C库并使用SWIG https en wikipedia org wiki SWIG创建一
  • 指向字节数组的指针

    由于 Misra C 的要求 我的一位同事想要使用指针声明 但我遇到了一些问题 Misra 安全关键指南 不会让我们纯粹的程序员使用指针 但会让我们对数组字节进行操作 他打算获取一个指向字节数组的指针 因此我们不会在堆栈上传递实际的数组 T
  • 无法加载文件或程序集“EntityFramework,版本=6.0.0.0”

    我究竟做错了什么 我该如何解决这个问题 我有一个包含多个项目的解决方案 它是一个 MVC NET 4 5 Web 应用程序 在调试模式下启动后调用其中一个项目时 出现此错误 导致此错误的项目具有以下参考 两个都是版本6 0 0 0 应用程序
  • 更改 IdentityServer4 实体框架表名称

    我正在尝试更改由 IdentityServer4 的 PersistedGrantDb 和 ConfigurationDb 创建的默认表名称 并让实体框架生成正确的 SQL 例如 而不是使用实体IdentityServer4 EntityF
  • 在VisualStudio DTE中,如何获取ActiveDocument的内容?

    我正在 VisualStudio 中编写脚本 并尝试获取当前 ActiveDocument 的内容 这是我当前的解决方案 var visualStudio new API VisualStudio 2010 var vsDTE visual
  • 系统错误 124 - SHFileOperation 的 ERROR_INVALID_LEVEL

    我在使用时遇到问题SHFileOperation SHFileOperation SHFILEOPSTRUCT https stackoverflow com questions 9191415 shfileoperation shfile
  • 正确使用“extern”关键字

    有一些来源 书籍 在线材料 解释了extern如下 extern int i declaration has extern int i 1 definition specified by the absence of extern 并且有支
  • 使用(linq to sql)更新错误

    我有两个表 通过外键 CarrierID 绑定 Carrier CarrierID CarrierName CarrierID 1 CarrierName DHL CarrierID 2 CarrierName Fedex Vendor V
  • 相当于 C# 中 Java 的“ByteBuffer.putType()”

    我正在尝试通过从 Java 移植代码来格式化 C 中的字节数组 在 Java 中 使用方法 buf putInt value buf putShort buf putDouble 等等 但我不知道如何将其移植到 C 我尝试过 MemoryS
  • 从 C 线程调用 Python 代码

    我对从 C 或 C 线程调用 Python 代码时如何确保线程安全感到非常困惑 The Python 文档 http docs python org c api init html non python created threads似乎是
  • 如何访问窗口?

    我正在尝试使用其句柄访问特定窗口 即System IntPtr value Getting the process of Visual Studio program var process Process GetProcessesByNam
  • 纯虚函数可能没有内联定义。为什么?

    纯虚函数是那些虚函数并且具有纯说明符 0 第 10 4 条第 2 款C 03 的内容告诉我们什么是抽象类 顺便说一句 如下 注意 函数声明不能 同时提供纯说明符和定义 尾注 示例 struct C virtual void f 0 ill
  • 如何获取 QIcon 的文件/资源​​路径

    假设我做了这样的事情 QIcon myIcon resources icon ico 我稍后如何确定该图标的路径 例如 QString path myIcon getPath 问题是 没有getPath 会员 我找不到类似的东西 但肯定有办
  • C# 粘贴到文本框时检查剪贴板中的字符

    有没有一些方法可以在粘贴到文本框 C 之前仅检查剪贴板中的字符 Ctrl V 和右键单击 gt 粘贴 但不使用 MaskedTextbox 在文本框文本更改中添加规则以仅接受数字 例如 private string value privat
  • FindAsync 很慢,但是延迟加载很快

    在我的代码中 我曾经使用加载相关实体await FindAsync 希望我能更好地遵守 C 异步指南 var activeTemplate await exec DbContext FormTemplates FindAsync exec

随机推荐

  • 如何在 Vuejs 组件中应用过滤器?

    如果我有一个简单的过滤器 请说 Vue filter foo function value return value replace foo g bar 还有一个简单的组件 Vue component example props msg S
  • 可以在网站上嵌入 Github 问题列表(带有特定标签)吗?

    有谁知道有一种简单的方法可以将带有特定标签的问题列表从 github 嵌入到网站上吗 这是为了在项目网站上嵌入未解决的错误列表 使用 jQuery 的解决方案 有一种方法可以轻松地使用 github api 仅使用 javascript 无
  • 固定位置在 Safari 7 中不起作用

    我有一个固定位置的 div 当我滚动时它位于屏幕底部 而不是移动 然而 在 Safari 上 这个 div 的行为就像是绝对定位的 并随着其余内容一起上下移动 当我单击 检查元素 时 编程的 所需的 位置会突出显示 而不是视觉 实际 位置
  • 使用 Python min() max() 避免数值的字典顺序

    我有一个脚本可以从一组值中提取随机数 然而 今天它崩溃了 因为min and max 按字典顺序对值进行排序 因此 200 被视为大于 10000 我怎样才能避免这里的字典顺序 Len关键是在正确的轨道上 但并不完全正确 我找不到任何其他有
  • 使用 istream 从命名管道读取

    是否可以使用 C stl 从命名管道 mkfifo 读取 使用流 因此没有提前定义char buffer MAX SIZE 用于读操作 我想读取直到缓冲区结束并将结果放入std string 当前方法 bytes read fd buffe
  • 如何在iOS中检索ssl服务器证书?

    我希望能够获得 ssl 证书 如果可能的话 链 以便能够显示可分辨名称并确定它是否是 EV 证书 通过证书策略检测 EV 证书 维基百科 http en wikipedia org wiki Extended Validation Cert
  • 如何使用 Python 获取 Selenium Webdriver 上的会话 ID?

    我正在使用 SauceLabs 我需要 sessionId 来获取那里的作业 ID 并使用它在测试执行期间设置通过 失败状态 如何使用 python 获取会话 ID 找到了 Selenium Remote Webdriver session
  • 如何用 2 列制作 R 图例?

    我想在我的图表上制作一个图例 它是由plot 功能 原本的legend 函数将生成一个只有 1 列的列表 我怎样才能制作一个有两列的图例 我无法找到一种方法可以在一次调用中做到这一点legend对于标准地块 这是一个选项 绘制两个单独的图例
  • 在 JNA 中创建一个本机 Windows 窗口和一些带有 GWL_WNDPROC 的 GetWindowLong

    再会 我已经使用 JNA 与 Windows API 交互有一段时间了 现在我在创建窗口时陷入困境 据我做了以下事情 1 创建了现有窗口的子窗口并为其获取了有效的处理程序 2 了解Windows 中的每个窗口都有一个不间断的消息分发循环 3
  • 我可以更新 WebStorm 中的 TypeScript 版本吗?

    我目前正在使用 WebStorm 10 0 4 最新的 TypeScript 版本是 1 5 3 但在 WebStorm 中存在旧版本的 TypeScript 如何在 WebStorm 中更新我的 TypeScript 版本 在默认设置对话
  • 当路径不存在时的 SHParseDisplayName

    我正在开发一个替代品IFileOpenDialog and IFileSaveDialog 我几乎让它工作了 至少IFileOpenDialog 但是当我想返回时IShellItem代表新文件名 用户选择保存在GetResult 方法 我无
  • 删除注册表键值

    在 MSDN 中 它说在调用更改正在枚举的注册表项的函数时不应使用 RegEnumValue 那么这也适用于删除注册表项值吗 就像这段代码所做的那样 if RegOpenKeyEx m hkey m path c str 0 KEY ALL
  • Gradle:在自定义独立插件中使用“maven-publish”插件

    在我的第三个问题之后 几乎感觉像是在发垃圾邮件 因为我的问题看起来很微不足道 但我在 Gradle 文档中找不到对我的案例有任何帮助 而且关于 stackoverflow 的其他问题也没有帮助 这次 我无法使用maven publish插件
  • 如何在 Perl 中引用长字符串?

    我通常使用简单的引号 但有时我会得到很长的行 我无法中断并且还需要使用转义字符 所以我得到如下内容 my str select query accession query tag hit accession hit tag signific
  • Tesseract 对阿拉伯语单词/字母不返回任何内容

    我已经安装了 Pytesseract 它可以完美地处理法语 英语文本以及数字 但是当我尝试阅读任何阿拉伯文本 字母时 它不会返回任何内容 这是我使用过的代码 try from PIL import Image except ImportEr
  • 考虑到可扩展性的基于组件的应用程序:OSGi 还是 Akka?

    在我的硕士论文中 我正在开发一个用于销售大型活动门票的应用程序框架 我的主要要求是可修改性 可扩展性和性能 我的客户 活动组织者 应该能够在运行时轻松替换组件并添加功能 此类组件的一个示例是座位分配组件 我的导师说看看 OSGi 松散耦合的
  • 具有多个引用的 postgresql cte 仅运行一次

    只是想知道 在 pgsql cte 中是否会创建临时表 并且每次加入查询时都不会重新执行 cte 下面是同一查询的 pgsql 和 sql server 版本 sql server 需要 6 秒 pgsql 只需要 3 秒 这是侥幸还是永远
  • 使用 D3.js 在矩形上显示文本

    I am developing Normalized stacked bar chart using d3 js and trying to append a text on rect It is getting appended when
  • 为什么启动应用程序时会看到双状态栏?

    当启动我的 iPhone 应用程序时 我同时看到 Default png 中的状态栏和操作系统的内置状态栏 而不仅仅是看到操作系统的状态栏 我做错了什么 将 Default png 的大小调整为 320x480 如果您的 Default p
  • 将 html 字符串拆分为 N 部分

    有没有人有一个使用 C 拆分 html 字符串 来自tinymce 编辑器 并将其拆分为 N 个部分的示例 我需要均匀地分割字符串而不分割单词 我正在考虑只拆分 html 并使用 HtmlAgilityPack 来尝试修复损坏的标签 虽然我