중첩 된 요소 (주로 div 와 p 요소)를 html로 가지고 있는데, 동일한 html을 반환해야하지만 주어진 수의 문자로 substring'ed해야합니다. 분명히 문자 수는 html 태그를 통해 열거해서는 안되며 각 html 요소의 InnerText 문자 만 계산해야합니다. HTML 결과는 올바른 구조를 유지해야합니다. 유효한 HTML을 유지하려면 닫기 태그가 있어야합니다.
샘플 입력 :
<div>
<p>some text</p>
<p>some more text some more text some more text some more text some more text</p>
<div>
<p>some more text some more text some more text some more text some more text</p>
<p>some more text some more text some more text some more text some more text</p>
</div>
</div>
int length = 16
이 주어지면 출력은 다음과 같이 보일 것입니다 :
<div>
<p>some text</p> // 9 characters in the InnerText here
<p>some mo</p> // 7 characters in the InnerText here; 9 + 7 = 16;
</div>
글자 수 (공백 포함)는 16입니다. 글자 수가 가변 length
도달했기 때문에 후속 <div>
는 삭제됩니다. 출력 html은 여전히 유효합니다.
나는 다음을 시도했지만 실제로 작동하지는 않는다. 출력 결과가 예상대로 아닙니다. 일부 html 요소가 반복됩니다.
public static string SubstringHtml(this string html, int length)
{
HtmlDocument doc = new HtmlDocument();
doc.LoadHtml(html);
int totalLength = 0;
StringBuilder output = new StringBuilder();
foreach (var node in doc.DocumentNode.Descendants())
{
totalLength += node.InnerText.Length;
if(totalLength >= length)
{
int difference = totalLength - length;
string lastPiece = node.InnerText.ToString().Substring(0, difference);
output.Append(lastPiece);
break;
}
else
{
output.Append(node.InnerHtml);
}
}
return output.ToString();
}
최신 정보
@SergeBelov는 첫 번째 샘플 입력에 사용할 수있는 솔루션을 제공했지만 추가 테스팅에서는 아래 입력과 같은 문제가있었습니다.
샘플 입력 # 2 :
some more text some more text
<div>
<p>some text</p>
<p>some more text some more text some more text some more text some more text</
</div>
그 변수 int maxLength = 7;
출력은 몇 개월 과 동일해야합니다. 이 코드 때문에 ParentNode = null
경우처럼 작동하지 않습니다.
lastNode
.Node
.ParentNode
.ReplaceChild(HtmlNode.CreateNode(lastNodeText.InnerText.Substring(0, lastNode.NodeLength - lastNode.TotalLength + maxLength)), lastNode.Node);
InnterText 속성이 읽기 전용이므로 새 HtmlNode를 만드는 것이 도움이되지 않습니다.
아래의 소형 콘솔 프로그램은 하나의 가능한 접근법을 보여줍니다.
업데이트 : 이것은 첫 번째 텍스트 노드와 함께 계속 작동해야합니다. 아마도 Trim()
은 아래와 같이 공백을 제거해야합니다.
static void Main(string[] args)
{
int maxLength = 9;
string input = @"
some more text some more text
<div>
<p>some text</p>
<p>some more text some more text some more text some more text some more text</
</div>";
var doc = new HtmlDocument();
doc.LoadHtml(input);
// Get text nodes with the appropriate running total
var acc = 0;
var nodes = doc.DocumentNode
.Descendants()
.Where(n => n.NodeType == HtmlNodeType.Text && n.InnerText.Trim().Length > 0)
.Select(n =>
{
var length = n.InnerText.Trim().Length;
acc += length;
return new { Node = n, TotalLength = acc, NodeLength = length };
})
.TakeWhile(n => (n.TotalLength - n.NodeLength) < maxLength)
.ToList();
// Select element nodes we intend to keep
var nodesToKeep = nodes
.SelectMany(n => n.Node.AncestorsAndSelf()
.Where(m => m.NodeType == HtmlNodeType.Element));
// Select and remove element nodes we don't need
var nodesToDrop = doc.DocumentNode
.Descendants()
.Where(m => m.NodeType == HtmlNodeType.Element)
.Except(nodesToKeep)
.ToList();
foreach (var r in nodesToDrop)
r.Remove();
// Shorten the last node as required
var lastNode = nodes.Last();
var lastNodeText = lastNode.Node;
var text = lastNodeText.InnerText.Trim().Substring(0,
lastNode.NodeLength - lastNode.TotalLength + maxLength);
lastNodeText
.ParentNode
.ReplaceChild(HtmlNode.CreateNode(text), lastNodeText);
doc.Save(Console.Out);
}