/// <summary> /// Возвращает входную строку, из которой удалены все подстроки, начинающиеся с указанного начального токена /// и заканчивающиеся ближайшим указанным конечным токеном. /// </summary> /// <param name="Input">Входящая строка, в которой происходит поиск.</param> /// <param name="StartToken">Начальный токен (подстрока), которым начинается удаляемая подстрока. Не может быть NULL или пустым.</param> /// <param name="EndToken">Конечный токен (подстрока), которым оканчивается удаляемая подстрока. Не может быть NULL или пустым.</param> /// <param name="RemoveTokens">Определяет, следует ли удалить начальный и конечный токены вместе с удаляемой подстрокой (true) или же их следует оставить (false)</param> /// <param name="ComparisonType">Опции сравнения строк между собой</param> /// <returns>Новая строка. Если не найдено ни одной пары начальныго и конечного токенов, возвращается оригинальная строка. /// Если начальный и конечный токены одинаковы, или исходная строка является NULL, пустой строкой либо содержит лишь пробелы, /// либо хотя бы один из токенов является NULL или пустой строкой, метод выбрасывает исключение.</returns> public static String RemoveFromStartToEndToken(String Input, String StartToken, String EndToken, Boolean RemoveTokens, StringComparison ComparisonType) { if (Input.IsStringNullEmptyWhiteSpace() == true) throw new ArgumentException("Входная строка является NULL, пустой строкой либо состоит лишь из одних пробелов", "Input"); if (StartToken.IsStringNullOrEmpty() == true) throw new ArgumentException("Начальный токен является NULL или пустой строкой", "StartToken"); if (EndToken.IsStringNullOrEmpty() == true) throw new ArgumentException("Конечный токен является NULL или пустой строкой", "EndToken"); if (String.Compare(StartToken, EndToken, ComparisonType) == 0) { throw new ArgumentException("Начальный и конечный токены должны быть разными с учётом указнных опций сравнения"); } Int32 current_offset = 0; StringBuilder sb = new StringBuilder(Input.Length); while (true) { Int32 start_index = Input.IndexOf(StartToken, current_offset, ComparisonType); if (start_index < 0) { break; } Int32 end_index = Input.IndexOf(EndToken, start_index, ComparisonType); if (end_index < 0) { break; } String slice; if (RemoveTokens) { slice = Input.SubstringWithEnd(current_offset, start_index); } else { slice = Input.SubstringWithEnd(current_offset, start_index + StartToken.Length) + Input.Substring(end_index, EndToken.Length); } sb.Append(slice); current_offset = end_index + EndToken.Length; } sb.Append(Input.Substring(current_offset)); return sb.ToString(); }
/// <summary> /// Возвращает набор индексов начала и конца всех вхождений искомого шаблона в указанной строке, начиная поиск с указанного индекса. /// Подстрока считается совпавшей с шаблоном тогда и только тогда, если начинается с указанного токена, оканчивается указанным токеном, и между токенами /// содержит указанные обязательные и необязательные символы. Если между токенами содержатся какие-либо другие символы, /// или же не содержится хотя бы одного из обязательных символов, то такая подстрока отбраковывается. /// </summary> /// <param name="Input">Входная строка, в которой происходит поиск шаблона. Если NULL или пустая, будет выброшено исключение.</param> /// <param name="StartToken">Начальный токен, которым должен начинаться искомый шаблон. Если NULL или пустой, будет выброшено исключение.</param> /// <param name="EndToken">Конечный токен, которым должен заканчиваться искомый шаблон. Если NULL или пустой, будет выброшено исключение.</param> /// <param name="StartIndex">Позиция (индекс), с которой включительно начинается анализ строки. Если меньше 0, будет приведён к 0. /// Если больше длины строки, будет выброшено исключение.</param> /// <param name="RequiredInnerSymbols">Массив обязательных символов, все из которых должны присутствовать между начальным и конечным токеном. /// Дублирующие символы игнорируются, учитываются только уникальные. Если NULL или пустой, предполагается, что обязательных символов нет.</param> /// <param name="OptionalInnerSymbols">Массив допустимых символов, которые могут, но не обязательны присутствовать между начальным и конечным токеном. /// Дублирующие символы игнорируются, учитываются только уникальные. Если NULL или пустой, предполагается, что обязательных символов нет.</param> /// <param name="CompareOptions">Опции сравнения строк между собой</param> /// <returns>Словарь, где один элемент представляет индексы одного найденного токена: ключ содержит индекс начала токена, а значение - индекс его конца. /// Если ни одного токена не найдено, возвращается пустой словарь.</returns> public static Dictionary<Int32, Int32> IndexesOfTemplate(String Input, String StartToken, String EndToken, Int32 StartIndex, Char[] RequiredInnerSymbols, Char[] OptionalInnerSymbols, StringComparison CompareOptions) { if (Input.IsStringNullOrEmpty() == true) { throw new ArgumentException("Входная строка не может быть NULL или пустой", "Input"); } if (StartToken.IsStringNullOrEmpty() == true) throw new ArgumentException("Начальный токен не может быть NULL или пустой строкой", "StartToken"); if (EndToken.IsStringNullOrEmpty() == true) throw new ArgumentException("Конечный токен не может быть NULL или пустой строкой", "EndToken"); if (StartIndex >= Input.Length) { throw new ArgumentOutOfRangeException("StartIndex", StartIndex, String.Format("Позиция начала поиска '{0}' превышает длину строки '{1}'", StartIndex, Input.Length)); } if (StartIndex < 0) { StartIndex = 0; } Int32 comp_value = (Int32)CompareOptions; if (comp_value < 0 || comp_value > 5) { throw new InvalidEnumArgumentException("CompareOptions", comp_value, typeof(StringComparison)); } if (RequiredInnerSymbols.IsNullOrEmpty() == true && OptionalInnerSymbols.IsNullOrEmpty() == true) { throw new ArgumentException("Массивы обязательных и допустимых символов не могут быть одновременно NULL или пустыми. " + String.Format("RequiredInnerSymbols: {0}, OptionalInnerSymbols: {1}.", RequiredInnerSymbols.ToStringS("NULL", "пустой"), OptionalInnerSymbols.ToStringS("NULL", "пустой"))); } Dictionary<Int32, Int32> output = new Dictionary<int, int>(); Int32 start_token_length = StartToken.Length; Int32 end_token_length = EndToken.Length; HashSet<Char> unique_RequiredInnerSymbols = RequiredInnerSymbols.IsNullOrEmpty() == true ? new HashSet<Char>() : new HashSet<Char>(RequiredInnerSymbols); HashSet<Char> unique_OptionalInnerSymbols = OptionalInnerSymbols.IsNullOrEmpty() == true ? new HashSet<Char>() : new HashSet<Char>(OptionalInnerSymbols); Int32 offset = StartIndex; Dictionary<Char, Boolean> temp_dict = unique_RequiredInnerSymbols.Any() == true ? unique_RequiredInnerSymbols.ToDictionary((Char item) => item, (Char item) => false) : null; while (true) { Int32 start_index = Input.IndexOf(StartToken, offset, CompareOptions); if (start_index < 0) { break; } offset = start_index + start_token_length; Int32 end_index = Input.IndexOf(EndToken, offset, CompareOptions); if (end_index < 0) { break; } #if Debug String temp_substring = Input.SubstringWithEnd(offset, end_index); #endif Boolean fail = false; for (Int32 i = offset; i < end_index; i++) { Char ch = Input[i]; if (ch.IsIn(unique_RequiredInnerSymbols) == false && ch.IsIn(unique_OptionalInnerSymbols) == false) { fail = true; break; } if (unique_RequiredInnerSymbols.Any() == true && ch.IsIn(unique_RequiredInnerSymbols) == true) { temp_dict[ch] = true; } } if (fail == true || (temp_dict != null && temp_dict.All(item => item.Value == true) == false)) { continue; } offset = end_index + end_token_length - 1; output.Add(start_index, offset); } return output; }
public String SubstringWithEnd(String Input, Int32 StartIndex, Int32 EndIndex, Boolean IncludeStart, Boolean IncludeEnd, Boolean UntilEnd) { return Input.SubstringWithEnd(StartIndex, EndIndex, IncludeStart, IncludeEnd, UntilEnd); }
/// <summary> /// Экранирует во входной строке все тэги <script> и </script> для защиты от самых распространённых XSS-инъекций. /// </summary> /// <param name="InputHTML"></param> /// <returns></returns> public static String SecureScriptXSS(String InputHTML) { if (InputHTML.HasVisibleChars() == false) { return InputHTML; } if (StringTools.ContainsHelpers.ContainsAllOf(InputHTML, new char[] { '<', '>' }) == false) { return InputHTML; } List<Substring> tags_with_positions = StringTools.SubstringHelpers.GetInnerStringsBetweenTokens(InputHTML, "<", ">", 0, false, StringComparison.OrdinalIgnoreCase); if (tags_with_positions.Any() == false) { return InputHTML; } StringBuilder output = new StringBuilder(InputHTML.Length); Int32 start_position = 0; foreach (Substring one_possible_tag in tags_with_positions) { String tag_name; HtmlTools.HtmlTagType tag_type = HtmlTools.ValidateHtmlTag("<" + one_possible_tag.Value + ">", out tag_name); if (tag_type == HtmlTagType.NotTag || tag_name.Equals("script", StringComparison.OrdinalIgnoreCase) == false) { output.Append(InputHTML.SubstringWithEnd(start_position, one_possible_tag.StartIndex + one_possible_tag.Value.Length + 1)); } else { if (tag_type == HtmlTagType.PairOpen) { output.Append(InputHTML.Substring(start_position, one_possible_tag.StartIndex - 1 - start_position)); output.Append("<script>"); } else if (tag_type == HtmlTagType.PairClose) { output.Append(InputHTML.Substring(start_position, one_possible_tag.StartIndex - 1 - start_position)); output.Append("</script>"); } else if (tag_type == HtmlTagType.Single) { output.Append(InputHTML.Substring(start_position, one_possible_tag.StartIndex - 1 - start_position)); output.Append("<script/>"); } else { throw new UnreachableCodeException(); } } start_position = one_possible_tag.StartIndex + one_possible_tag.Value.Length + 1; } output.Append(InputHTML.Substring(start_position)); return output.ToString(); }
/// <summary> /// Возвращает индексы начала и конца первого вхождения указанного шаблона во входящей строке, начиная поиск с указанного индекса. /// Подстрока считается совпавшей с шаблоном тогда и только тогда, когда она начинается с начального токена, заканчивается конечным токеном и /// содержит между ними лишь и только все указанные внутренние символы. Если между начальным и конечным токенами содержатся /// какие-либо другие символы, или хотя бы один из указанных внутренних символов не содержится между начальным и конечным, то такая подстрока отбраковывается. /// </summary> /// <param name="Input">Входная строка, в которой происходит поиск шаблона. Если NULL или пустая, будет выброшено исключение.</param> /// <param name="StartToken">Начальный токен, которым должен начинаться искомый шаблон. Если NULL или пустой, будет выброшено исключение.</param> /// <param name="EndToken">Конечный токен, которым должен заканчиваться искомый шаблон. Если NULL или пустой, будет выброшено исключение.</param> /// <param name="StartIndex">Позиция (индекс), с которой включительно начинается анализ строки. Если меньше 0, будет приведён к 0. /// Если больше длины строки, будет выброшено исключение.</param> /// <param name="InnerSymbols">Символы, из которых состоит "промежность" искомого шаблона между начальными и конечными токенами. /// Чтобы искомый шаблон считался валидным, его "промежность" должна состоять лишь и только из всех указанных символов. /// Дублирующие символы игнорируются, учитываются только уникальные. Если массив является NULL или пуст, будет выброшено исключение.</param> /// <returns>Связка из индекса начала и индекса конца первого вхождения указанного шаблона во входящей строке, начиная с 0 включительно. Если шаблон не найден, возвращает два -1.</returns> public static KeyValuePair<Int32, Int32> IndexesOfTemplateFirstOccurence(String Input, String StartToken, String EndToken, Int32 StartIndex, params Char[] InnerSymbols) { if (Input.IsStringNullOrEmpty() == true) { throw new ArgumentException("Входная строка не может быть NULL или пустой", "Input"); } if (StartToken.IsStringNullOrEmpty() == true) throw new ArgumentException("Начальный токен не может быть NULL или пустой строкой", "StartToken"); if (EndToken.IsStringNullOrEmpty() == true) throw new ArgumentException("Конечный токен не может быть NULL или пустой строкой", "EndToken"); if (String.Compare(StartToken, EndToken, StringComparison.InvariantCultureIgnoreCase) == 0) throw new ArgumentException("Начальный и конечный токены должны быть разными, не считая различий в регистре"); if (Input.Contains(StartToken) == false) throw new ArgumentException("Начальный токен не содержится в искомой строке", "StartToken"); if (Input.Contains(EndToken) == false) throw new ArgumentException("Конечный токен не содержится в искомой строке", "EndToken"); if (InnerSymbols.IsNullOrEmpty() == true) { throw new ArgumentException("Массив с внутренними символами является NULL или пуст", "InnerSymbols"); } if (StartIndex >= Input.Length) { throw new ArgumentOutOfRangeException("StartIndex", StartIndex, String.Format("Позиция начала поиска '{0}' превышает длину строки '{1}'", StartIndex, Input.Length)); } if (StartIndex < 0) { StartIndex = 0; } Int32 start_token_length = StartToken.Length; Int32 end_token_length = EndToken.Length; Int32 offset = StartIndex; Int32 start_index; Int32 end_index; String temp_substring; while (true) { start_index = Input.IndexOf(StartToken, offset, StringComparison.InvariantCultureIgnoreCase); if (start_index < 0) { break; } offset = start_index + start_token_length; end_index = Input.IndexOf(EndToken, offset, StringComparison.InvariantCultureIgnoreCase); if (end_index < 0) { break; } temp_substring = Input.SubstringWithEnd(start_index + start_token_length, end_index); if (StringTools.ContainsHelpers.ContainsAllAndOnlyOf(temp_substring, InnerSymbols) == true) { return new KeyValuePair<int, int>(start_index, end_index + end_token_length); } } return new KeyValuePair<int, int>(-1, -1); }
/// <summary> /// Анализирует входную XML-разметку на наличие некорректных парных тэгов, и если такие найдены, то исправляет их в корректной последовательности. /// Если во входной строке нет некорректных парных тэгов или нет тэгов вообще, метод возвращает её без изменений. /// </summary> /// <param name="InputXML">Входная строка, содержащая предположительно некорректную XML-разметку</param> /// <returns>Гарантированно корректная XML-разметка</returns> public static String FixBrokenXMLTags(String InputXML) { if (InputXML.IsStringNullEmptyWhiteSpace() == true) { return InputXML; } if (StringTools.ContainsHelpers.ContainsAllOf(InputXML, new char[] { '<', '>' }) == false) { return InputXML; } List<Substring> tags_with_positions = StringTools.SubstringHelpers.GetInnerStringsBetweenTokens (InputXML, "<", ">", 0, false, StringComparison.OrdinalIgnoreCase); if (tags_with_positions.Any() == false) { return InputXML; } StringBuilder output = new StringBuilder(InputXML.Length); Stack<String> open_tags = new Stack<String>(); Int32 start_position = 0; foreach (Substring one_possible_tag in tags_with_positions) { String tag_name; HtmlTools.HtmlTagType tag_type = HtmlTools.ValidateHtmlTag("<" + one_possible_tag.Value + ">", out tag_name); if (tag_type != HtmlTagType.PairClose)//если предполагаемый тэг не является тэгом, или одиночный, или парный открывающий { //добавляем в выводную строку часть исходной строки, начиная от позиции поиска и заканчивая концом просканированной части output.Append(InputXML.SubstringWithEnd(start_position, one_possible_tag.StartIndex + one_possible_tag.Value.Length + 1)); //устанавливаем новое значение позиции поиска, равное позиции начала вхождения тела потенциального тэга и заканчивая концом закрывающей скобки, следуемой после тела потенциального тэга start_position = one_possible_tag.StartIndex + one_possible_tag.Value.Length + 1; if (tag_type == HtmlTagType.PairOpen)//если предполагаемый тэг является парным открывающим { open_tags.Push(tag_name);//добавляем его в стэк } } else//если предполагаемый тэг является парным закрывающим { if (open_tags.Any() == false || open_tags.Peek() != tag_name)//закрывающий тэг не соответствует последнему открывающему или нет незакрытых тэгов { //добавляем в выводную строку часть исходной строки, начиная от позиции поиска и заканчивая началом открывающей скобки неоткрытого закрывающего тэга if (one_possible_tag.StartIndex - 1 > start_position) { output.Append(InputXML.SubstringWithEnd(start_position, one_possible_tag.StartIndex - 1)); } //устанавливаем новое значение позиции поиска, равное позиции начала вхождения тела потенциального тэга и заканчивая концом закрывающей скобки, //следуемой после тела потенциального тэга start_position = one_possible_tag.StartIndex + one_possible_tag.Value.Length + 1; } else//закрывающий тэг соответствует последнему открывающему { //удалить открывающий из стэка open_tags.Pop(); //добавляем в выводную строку часть исходной строки, начиная от позиции поиска и заканчивая концом просканированной части output.Append(InputXML.SubstringWithEnd(start_position, one_possible_tag.StartIndex + one_possible_tag.Value.Length + 1)); //устанавливаем новое значение позиции поиска, равное позиции начала вхождения тела потенциального тэга и заканчивая концом закрывающей скобки, //следуемой после тела потенциального тэга start_position = one_possible_tag.StartIndex + one_possible_tag.Value.Length + 1; } } } if (start_position < InputXML.Length) { output.Append(InputXML.Substring(start_position)); } while (open_tags.Any() == true) { output.Append("</" + open_tags.Pop() + ">"); } return output.ToString(); }
/// <summary> /// Возвращает все атрибуты вместе с их соответствующими значениями для указанного по имени тэга. /// Если тэгов несколько, будут возвращены атрибуты для первого встретившегося тэга. /// </summary> /// <param name="InputHTML">Входная HTML-содержащая строка. Не может быть NULL, пустой строкой или не содержать цифробуквенных символов.</param> /// <param name="TargetTagName">Имя целевого тэга, все атрибуты со значениями которого следует возвратить. /// Не может быть NULL, пустой строкой или не содержать цифробуквенных символов.</param> /// <param name="StartIndex">Начальная позиция входной HTML-содержащей строки, с которой следует начать поиск. Если 0 - поиск ведётся с начала. /// Если меньше 0 или больше длины исходной строки, выбрасывается исключение.</param> /// <returns>Список атрибутов вместе с их соответствующими значениями для указанного тэга в виде словаря, /// где ключ - атрибут, а его значение - значение атрибута. Если целевой тэг не найден, возвращает NULL. /// Если тэг найден, но он не содержит атрибутов, возвращается пустой словарь.</returns> public static Dictionary<String, String> GetAttributesForTag(String InputHTML, String TargetTagName, Int32 StartIndex) { if(InputHTML == null) {throw new ArgumentNullException("InputHTML");} if(InputHTML.HasAlphaNumericChars()==false) { throw new ArgumentException("Входная HTML-содержащая строка не содержит ни одной буквы или цифры и не является валидным HTML документом", "InputHTML"); } if (TargetTagName.HasAlphaNumericChars() == false) { throw new ArgumentException("Имя целевого тэга некорректно, так как не содержит ни одной буквы или цифры", "TargetTagName");} if (StartIndex < 0) { throw new ArgumentOutOfRangeException("StartIndex", StartIndex, "Начальная позиция не может быть меньше 0"); } if (StartIndex >= InputHTML.Length) { throw new ArgumentOutOfRangeException("StartIndex", StartIndex, String.Format("Начальная позиция ('{0}') не может быть больше или равна длине строки ('{1}')", StartIndex, InputHTML.Length)); } String cleared_tag_name = TargetTagName.Trim().ToLowerInvariant(); if (cleared_tag_name.StartsWith("<") == false) { cleared_tag_name = "<" + cleared_tag_name; } Int32 tag_start_pos = InputHTML.IndexOf(cleared_tag_name, StartIndex, StringComparison.OrdinalIgnoreCase); if (tag_start_pos == -1) { return null; } Int32 closing_bracket_pos = InputHTML.IndexOf(">", tag_start_pos + cleared_tag_name.Length, StringComparison.OrdinalIgnoreCase); if (InputHTML[closing_bracket_pos - 1] == '/') { closing_bracket_pos = closing_bracket_pos - 1; } string substring_with_attributes = InputHTML.SubstringWithEnd(tag_start_pos + cleared_tag_name.Length, closing_bracket_pos, false, false, false).Trim(); Dictionary<String, String> output = new Dictionary<String, String>(StringComparer.OrdinalIgnoreCase); if (substring_with_attributes.IsStringNullEmptyWhiteSpace()) { return output; } StringBuilder attribute_key_buffer = new StringBuilder(substring_with_attributes.Length); StringBuilder attribute_value_buffer = new StringBuilder(substring_with_attributes.Length); Boolean key_is_now = true; Boolean value_is_now = false; Boolean found_equal_sign = false; Boolean finished_pair = false; Boolean inside_quotes = false; Boolean value_without_quotes = false; Boolean whitespace_previous = false; foreach (Char one in substring_with_attributes) { if (Char.IsWhiteSpace(one)) { whitespace_previous = true; if (value_without_quotes == true) { value_without_quotes = false; value_is_now = false; key_is_now = false; } else if (value_is_now == true) { attribute_value_buffer.Append(one); } else { key_is_now = false; } continue; } if (one == '=') { whitespace_previous = false; if (inside_quotes && value_is_now) { attribute_value_buffer.Append(one); } else { key_is_now = false; value_is_now = false; found_equal_sign = true; } continue; } if (one == '"' || one == '\'') { whitespace_previous = false; inside_quotes = !inside_quotes; if (found_equal_sign) { key_is_now = false; value_is_now = true; } else { key_is_now = false; value_is_now = false; String attribute_key = attribute_key_buffer.ToString(); if (output.ContainsKey(attribute_key) == false) { output.Add(attribute_key, attribute_value_buffer.ToString()); } attribute_key_buffer.Clean(); attribute_value_buffer.Clean(); finished_pair = true; } found_equal_sign = false; continue; } if (value_is_now == false && found_equal_sign == false && inside_quotes == false && finished_pair == false && whitespace_previous == true) { String attribute_key = attribute_key_buffer.ToString(); if (output.ContainsKey(attribute_key) == false) { output.Add(attribute_key, attribute_value_buffer.ToString()); } attribute_key_buffer.Clean(); attribute_value_buffer.Clean(); finished_pair = true; } whitespace_previous = false; if (finished_pair == false && found_equal_sign == true && inside_quotes == false) { found_equal_sign = false; value_is_now = true; value_without_quotes = true; } if (value_is_now) { attribute_value_buffer.Append(one); continue; } if (finished_pair) { finished_pair = false; key_is_now = true; } if ((Char.IsLetterOrDigit(one) || one == '-' || one == ':') && key_is_now) { attribute_key_buffer.Append(one); continue; } } if (attribute_key_buffer.Length > 0) { String attribute_key = attribute_key_buffer.ToString(); if (output.ContainsKey(attribute_key) == false) { output.Add(attribute_key, attribute_value_buffer.ToString()); } } return output; }
/// <summary> /// Удаляет из входной строки все парные HTML-тэги со всеми их атрибутами, которые не содержат внутри контента. /// HTML-тэги, содержащие вложенные пустые тэги, также удаляются. Одиночные тэги не удаляются. /// </summary> /// <param name="InputHTML">Входная строка</param> /// <returns>Новая очищенная от пустых парных тэгов строка</returns> public static String RemoveEmptyPairHTMLTags(String InputHTML) { if (InputHTML.HasVisibleChars() == false) { return InputHTML; } const Char opening_bracket = '<'; const Char closing_bracket = '>'; const String closing_token = "</"; const Char space = ' '; StringBuilder output = new StringBuilder(InputHTML.Length); Int32 offset = 0; while (true) { Int32 index_of_opening_bracket_opening_tag = InputHTML.IndexOf(opening_bracket, offset); if (index_of_opening_bracket_opening_tag == -1) { break; } Int32 index_of_closing_bracket_opening_tag = InputHTML.IndexOf(closing_bracket, index_of_opening_bracket_opening_tag + 1); if (index_of_closing_bracket_opening_tag == -1) { break; } String opening_tag = InputHTML.SubstringWithEnd(index_of_opening_bracket_opening_tag + 1, index_of_closing_bracket_opening_tag).Trim(); if (opening_tag.Contains(space) == true) { opening_tag = opening_tag.Split(new Char[1] { space }, StringSplitOptions.RemoveEmptyEntries)[0]; } Int32 index_of_opening_bracket_closing_tag = InputHTML.IndexOf(closing_token + opening_tag, index_of_closing_bracket_opening_tag + 1, StringComparison.OrdinalIgnoreCase); if (index_of_opening_bracket_closing_tag == -1) { String temp = InputHTML.SubstringWithEnd(offset, index_of_closing_bracket_opening_tag + 1); output.Append(temp); offset = index_of_closing_bracket_opening_tag + 1; continue; } Int32 index_of_closing_bracket_closing_tag = InputHTML.IndexOf(closing_bracket, index_of_opening_bracket_closing_tag + 1); if (index_of_closing_bracket_closing_tag == -1) { break; } #if Debug String closing_tag = InputHTML.SubstringWithEnd(index_of_opening_bracket_closing_tag + 2, index_of_closing_bracket_closing_tag).Trim(); #endif String between_offset_and_opening_tag = InputHTML.SubstringWithEnd(offset, index_of_opening_bracket_opening_tag); output.Append(between_offset_and_opening_tag); String inner = InputHTML.SubstringWithEnd(index_of_closing_bracket_opening_tag + 1, index_of_opening_bracket_closing_tag); if (inner.HasVisibleChars() == true) { String temp = RemoveEmptyPairHTMLTags(inner); if (temp.HasVisibleChars() == true) { String temp2 = InputHTML.SubstringWithEnd(index_of_opening_bracket_opening_tag, index_of_closing_bracket_opening_tag + 1) + temp + InputHTML.SubstringWithEnd(index_of_opening_bracket_closing_tag, index_of_closing_bracket_closing_tag + 1); output.Append(temp2); } } offset = index_of_closing_bracket_closing_tag + 1; } if (offset == 0) { output.Append(InputHTML); } else if (offset > 0 && offset < InputHTML.Length) { output.Append(InputHTML.Substring(offset + 1)); } String result = output.ToString(); return result; }
/// <summary> /// Удаляет из входной строки все одинарные и парные HTML-тэги с их атрибутами, оставляя их содержимое. Если входная строка не содержит HTML-тэгов, метод возвращает её без изменений. /// Удаляются также некорректно расположенные HTML-тэги (неоткрытые, незакрытые и перехлестывающиеся). Заменяет все HTML-пробелы и переносы строк на их символьные аналоги. /// </summary> /// <param name="InputHTML">Входная HTML-содержащая строка</param> /// <returns>Копия входной строки, не содержащая никаких HTML-тегов</returns> public static String IntelliRemoveHTMLTags(String InputHTML) { if (InputHTML.HasVisibleChars() == false) { return InputHTML; } if (StringTools.ContainsHelpers.ContainsAllOf(InputHTML, new char[] { '<', '>' }) == false) { return InputHTML; } List<Substring> tags_with_positions = StringTools.SubstringHelpers.GetInnerStringsBetweenTokens (InputHTML, "<", ">", 0, false, StringComparison.OrdinalIgnoreCase); if (tags_with_positions.Any() == false) { return InputHTML; } const String combined_enter = "\r\n", space = " "; StringBuilder temp = new StringBuilder(InputHTML.Length); Int32 start_position = 0; foreach (Substring one_possible_tag in tags_with_positions) { if (start_position < one_possible_tag.StartIndex - 1) { temp.Append(InputHTML.SubstringWithEnd(start_position, one_possible_tag.StartIndex - 1, true, false, true)); } if (one_possible_tag.Value.Contains("br", StringComparison.OrdinalIgnoreCase)==true && StringTools.ContainsHelpers.ContainsOnlyAllowed(one_possible_tag.Value, new char[] { ' ', '/', 'b', 'r', 'B', 'R' }) == true) { temp.Append(combined_enter); } start_position = one_possible_tag.StartIndex + one_possible_tag.Value.Length + 1; } String temp2 = InputHTML.Substring(start_position); if (temp2.HasVisibleChars() == true) { String temp3 = StringTools.SubstringHelpers.GetSubstringToToken(temp2, "<", false, StringTools.Direction.FromStartToEnd, StringComparison.OrdinalIgnoreCase); temp.Append(temp3); } else { temp.Append(temp2); } temp.Replace(" ", space).Replace(" ", space); return temp.ToString(); }
/// <summary> /// Удаляет из входной строки все одинарные и парные HTML-тэги с их атрибутами, оставляя их содержимое. Если входная строка не содержит HTML-тэгов, метод возвращает её без изменений. /// Удаляются также некорректно расположенные HTML-тэги (неоткрытые, незакрытые и перехлестывающиеся). /// </summary> /// <param name="InputHTML">Входная HTML-содержащая строка</param> /// <returns>Копия входной строки, не содержащая никаких HTML-тегов</returns> public static String RemoveHTMLTags(String InputHTML) { if (InputHTML.HasVisibleChars() == false) { return InputHTML; } List<Substring> tags_with_positions = StringTools.SubstringHelpers.GetInnerStringsBetweenTokens(InputHTML, "<", ">", 0, false, StringComparison.OrdinalIgnoreCase); if (tags_with_positions.Any() == false || tags_with_positions.TrueForAll((Substring item) => item == null)) { return InputHTML; } StringBuilder temp = new StringBuilder(InputHTML.Length); Int32 start_position = 0; foreach (Substring one_possible_tag in tags_with_positions) { if (one_possible_tag == null) { continue; } if (CommonTools.AreAllEqual<Int32>(start_position, one_possible_tag.StartIndex - 1, 0) == true || start_position == one_possible_tag.StartIndex - 1) { start_position = one_possible_tag.StartIndex + one_possible_tag.Value.Length + 1; continue; } temp.Append(InputHTML.SubstringWithEnd(start_position, one_possible_tag.StartIndex - 1, true, false, true)); start_position = one_possible_tag.StartIndex + one_possible_tag.Value.Length + 1; } String temp2 = InputHTML.Substring(start_position); if (temp2.HasVisibleChars() == true && StringTools.ContainsHelpers.ContainsAllOf(temp2, new char[] { '<', '>' }) == false) { String temp3 = StringTools.SubstringHelpers.GetSubstringToToken(temp2, "<", false, StringTools.Direction.FromStartToEnd, StringComparison.OrdinalIgnoreCase); temp.Append(temp3); } else { temp.Append(temp2); } return temp.ToString(); }