Существует много вопросов о том, как разделить теги HTML, но не многих на функциях/методах для закрытия их.
Вот ситуация. У меня есть 500 символьной Краткой информации о сообщениях (который включает теги HTML), но я только хочу первые 100 символов. Проблема состоит в том, если я усекаю сообщение, это могло бы быть посреди тега HTML..., который портит материал.
Принятие HTML является чем-то вроде этого:
<div class="bd">"Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. <br/>
<br/>Some Dates: April 30 - May 2, 2010 <br/>
<p>Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. <em>Duis aute irure dolor in reprehenderit</em> in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum. <br/>
</p>
For more information about Lorem Ipsum doemdloe, visit: <br/>
<a href="http://www.somesite.com" title="Some Conference">Some text link</a><br/>
</div>
Как я взял бы первые ~100 символов или так? (Хотя, идеально который был бы первым приблизительно 100 символов "СОДЕРЖАНИЯ" (промежуточный теги HTML)
Я принимаю лучший способ сделать, это было бы рекурсивным алгоритмом, который отслеживает теги HTML и добавляет любые теги, которые были бы усеченными, но это не может быть лучшим подходом.
Мои первые мысли используют рекурсию для подсчета вложенных тегов, и когда мы достигаем 100 символов, ищем следующее" <" и затем используем рекурсию для записи заключительных тегов HTML, необходимых оттуда.
Причина того, чтобы сделать это состоит в том, чтобы сделать краткое изложение существующих статей, не требуя, чтобы пользователь возвратился и предоставил сводки для всех статей. Я хочу сохранить форматирование HTML, если это возможно.
Примечание: Проигнорируйте, что HTML не является полностью семантическим. Это - то, с чем я должен иметь дело от моего WYSIWYG.
Я добавил потенциальное решение (который, кажется, работает), я полагаю, что другие столкнутся с этой проблемой также. Я не уверен, что это является лучшим..., и это, вероятно, не полностью устойчиво (на самом деле, я знаю, что это не), но я ценил бы любую обратную связь
Я предлагаю найти удобный обходчик HTML (тот, который позволяет перемещаться по HTML, как XML), а затем, начиная с самого начала, теги игнорируют сами теги и подсчитывать только данные в теге. Подсчитайте это к своему пределу, а затем, когда он будет достигнут, просто закройте каждый тег (я не могу думать о каких-либо тегах, которые не являются просто тегом / чем-то еще).
Это должно работать достаточно хорошо и быть довольно близким к тому, что вы ищете.
Это совершенно не похоже на ol'noggin, поэтому я предполагаю, что будут некоторые хитрые части, такие как отображаемые значения атрибутов (например, значения тегов ссылок).
Раньше я делал это с помощью регулярных выражений. Возьмите контент, удалите теги с помощью регулярного выражения, затем обрежьте его до желаемой длины.
Конечно, при этом удаляется весь HTML, чего я и хотел. Если вы хотите сохранить HTML, я бы подумал не закрывать открытые теги, а скорее удалить открытые теги.
Что, если вы проанализируете HTML в структуре DOM, а затем начнете обход в ширину или сначала в глубину, что вам нравится, собирая текст узлов, пока не достигнете 100 символов?
Я решил использовать собственное решение ... просто для того, чтобы сделать это.
Если кто-нибудь увидит какие-либо логические ошибки или неэффективность, дайте мне знать.
Я не знаю, лучший ли это подход ... но, похоже, он работает. Вероятно, есть случаи, когда это не работает ... и, скорее всего, выйдет из строя, если html неверен.
/// <summary>
/// Get the first n characters of some html text
/// </summary>
private string truncateTo(string s, int howMany, string ellipsis) {
// return entire string if it's more than n characters
if (s.Length < howMany)
return s;
Stack<string> elements = new Stack<string>();
StringBuilder sb = new StringBuilder();
int trueCount = 0;
for (int i = 0; i < s.Length; i++) {
if (s[i] == '<') {
StringBuilder elem = new StringBuilder();
bool selfclosing = false;
if (s[i + 1] == '/') {
elements.Pop(); // Take the previous element off the stack
while (s[i] != '>') {
i++;
}
}
else { // not a closing tag so get the element name
while (i < s.Length && s[i] != '>') {
if ((s[i] >= 'a' && s[i] <= 'z') || (s[i] >= 'A' && s[i] <= 'Z')) {
elem.Append(s[i]);
}
else if (s[i] == '/' || s[i] == ' ') {
// self closing tag or end of tag name. Find the end of tag
do {
if (s[i] == '/' && s[i + 1] == '>') {
// at the end of self-closing tag. Don't store
selfclosing = true;
}
i++;
} while (i < s.Length && s[i] != '>');
}
i++;
} // end while( != '>' )
if (!selfclosing)
elements.Push(elem.ToString());
}
}
else {
trueCount++;
if (trueCount > howMany) {
sb.Append(s.Substring(0, i - 1));
sb.Append(ellipsis);
while (elements.Count > 0) {
sb.AppendFormat("</{0}>", elements.Pop());
}
}
}
}
return sb.ToString();
}