C# 保留(或恢复)文本内容中的空白
使用AngleSharp处理一些HTML并提取元素的文本内容以供以后挖掘,我遇到了AngleSharp去除HTML标记的方式的问题。例如,我有一段类似这样的HTML(不包括换行符和制表符): 结果会是这样的:C# 保留(或恢复)文本内容中的空白,c#,anglesharp,C#,Anglesharp,使用AngleSharp处理一些HTML并提取元素的文本内容以供以后挖掘,我遇到了AngleSharp去除HTML标记的方式的问题。例如,我有一段类似这样的HTML(不包括换行符和制表符): 结果会是这样的: "...blah, blah, action.Typical blah, blah..." IEnumerable<string> ExtractChildNodes(INode node) { if (node.HasChildNodes) {
"...blah, blah, action.Typical blah, blah..."
IEnumerable<string> ExtractChildNodes(INode node)
{
if (node.HasChildNodes)
{
foreach (var c in node.ChildNodes)
{
foreach (var r in ExtractChildNodes(c))
{
yield return r;
}
}
}
else
{
yield return node.TextContent;
}
}
单词action
和Typical
被粉碎在一起,没有任何空格(因为它们之间只有html标记)。这让我的文本内容标记化工作受挫,因为action。典型的被视为一个单词而不是两个单词
当然,我可以运行搜索并替换(可能使用正则表达式),类似于(\S)\(\S)
,然后用$1替换它$2
但这需要像www.somecompany.com
这样的东西,然后将其分为www
、somecompany
和com
,我可能想保留这一点(或者如果www
和com
本身不太可能有用)。我可以排除带有多个点的单词,但网址可能显示为somecompany.com
(没有www
),或者您可能会遇到类似somebody@somecompany.com
有没有一个强有力的方法来解决这个问题?要在标记被剥离后至少保留一个空间?因此,解决这一问题的最佳方法似乎是递归根元素的子节点(而不是缺少文本节点的子节点),然后再次将它们全部连接起来。因此,鉴于:
var rootElem = myDoc.GetElementById("someId");
我可以创建如下函数:
"...blah, blah, action.Typical blah, blah..."
IEnumerable<string> ExtractChildNodes(INode node)
{
if (node.HasChildNodes)
{
foreach (var c in node.ChildNodes)
{
foreach (var r in ExtractChildNodes(c))
{
yield return r;
}
}
}
else
{
yield return node.TextContent;
}
}
这应该给我:
在动作
和典型
之间留有空格
这似乎不仅适用于某些情况,如单词
,还适用于一些自动关闭标签,如单词,甚至一些
单词
,使用正则表达式或类似的东西将是一种巨大的痛苦。您描述的方式很有效,除了一些您已经遇到的场景(例如,自动关闭标签)。因此,我提议如下:
- 文本节点将按字面意思获取
- 元素在其子节点w.r.t上迭代。
- 如果两个相邻元素产生内容,则插入一个空格
- 如果没有子节点,则查看元素是否特殊(如br),并放置一些代表性字符串(如换行符)
- 否则,例如,如果文本节点与元素相邻,则不会插入文本
因此,总体而言,以下实现应该完成这项工作:
String Stringify(INode node)
{
switch (node.NodeType)
{
case NodeType.Text:
return node.TextContent;
case NodeType.Element:
if (node.HasChildNodes)
{
var sb = new StringBuilder();
var isElement = false;
foreach (var child in node.ChildNodes)
{
var isPreviousElement = isElement;
var content = Stringify(child);
isElement = child.NodeType == NodeType.Element;
if (!String.IsNullOrEmpty(content) && isElement && isPreviousElement)
{
sb.Append(' ');
}
sb.Append(content);
}
return sb.ToString();
}
switch (node.NodeName.ToLowerInvariant())
{
case "br": return "\n";
}
goto default;
default:
return String.Empty;
}
}
这种实现的优点是,您可以根据自己的需要对其进行调整。例如,对于br
之类的标记,您可以轻松地输出空格而不是换行符。这真的是您正在解析的HTML吗(字面上)?因为如果是这样的话,AngleSharp肯定会在“action.”和“typical”之间有空格(事实上是一个新行和一些空格字符)。@FlorianRappl:实际上不是,我格式化它是为了清晰,但实际上它是一行,没有换行符或制表符。我想那部分有点混乱。我将在问题中澄清。是的,我猜是这样。正如我所说:否则,您将看到文本节点的文本。我将提供另一种可能对你有用的方法/答案。
"...blah, blah, action. Typical blah, blah..."
String Stringify(INode node)
{
switch (node.NodeType)
{
case NodeType.Text:
return node.TextContent;
case NodeType.Element:
if (node.HasChildNodes)
{
var sb = new StringBuilder();
var isElement = false;
foreach (var child in node.ChildNodes)
{
var isPreviousElement = isElement;
var content = Stringify(child);
isElement = child.NodeType == NodeType.Element;
if (!String.IsNullOrEmpty(content) && isElement && isPreviousElement)
{
sb.Append(' ');
}
sb.Append(content);
}
return sb.ToString();
}
switch (node.NodeName.ToLowerInvariant())
{
case "br": return "\n";
}
goto default;
default:
return String.Empty;
}
}