using System.Collections.Generic;
using System.Text.RegularExpressions;
public static void Main()
Console.WriteLine(FixHtmlTagsInText("123<span>456<b class=\"a\">789{012</b>345<b class=\"b\">678</b>901<b class=\"c\">234}[1]567</b>890</span>123"));
Console.WriteLine(FixHtmlTagsInText("Тут <span class=\"a\">{вариант ответа</span>}[1] и ещё один {вариант ответа}[2] да"));
public static readonly Regex ClickTestAnswer = new Regex(@"{(?<text>[^}]+)}\[(?<code>\d+)\]");
private static readonly Regex OpenTagRegex = new Regex(@"<(?<tag>[a-zA-Z]+)[^>]*>");
private static readonly Regex CloseTagRegex = new Regex(@"</(?<tag>[a-zA-Z]+)>");
public static string FixHtmlTagsInText(string text)
var answerMatches = ClickTestAnswer.Matches(text);
if (answerMatches.Count == 0)
var result = new StringBuilder();
for (var i = 0; i < answerMatches.Count; i++)
var currentMatch = answerMatches[i];
result.Append(text.Substring(startIndex, currentMatch.Index - startIndex));
startIndex += currentMatch.Length;
var openTags = OpenTagRegex.Matches(currentMatch.Value);
var closeTags = CloseTagRegex.Matches(currentMatch.Value);
var firstCloseTagMatch = closeTags.FirstOrDefault();
var lastOpenTagMatch = openTags.LastOrDefault();
if (firstCloseTagMatch == null && lastOpenTagMatch == null)
result.Append(currentMatch.Value);
startIndex += currentMatch.Length;
if (firstCloseTagMatch != null)
var closeTag = firstCloseTagMatch.Groups["tag"].Value;
if (!openTags.Any(x => x.Index < firstCloseTagMatch.Index && x.Groups["tag"].Value == closeTag))
result.Append(firstCloseTagMatch.Value);
var openingTag = OpenTagRegex.Matches(text.Substring(0, currentMatch.Index))
.Where(x => x.Groups["tag"].Value == closeTag && !x.Value.EndsWith("/>"))
throw new Exception("HTML-разметка текста содержит ошибки. Проверьте, чтобы всем закрывающим тэгам присутствовал парный открывающий.");
result.Append(openingTag.Value);
if (lastOpenTagMatch == null)
result.Append(currentMatch.Value.Substring(1, currentMatch.Value.Length - 1));
result.Append(currentMatch.Value.Substring(1, lastOpenTagMatch.Index + lastOpenTagMatch.Length - 1));
result.Append(currentMatch.Value.Substring(0, lastOpenTagMatch!.Index + lastOpenTagMatch.Length));
if (lastOpenTagMatch != null)
var openTag = lastOpenTagMatch.Groups["tag"].Value;
if (!closeTags.Any(x => x.Index > lastOpenTagMatch.Index && x.Groups["tag"].Value == openTag))
var lastOpenTagEndIndex = lastOpenTagMatch.Index + lastOpenTagMatch.Length;
result.Append(currentMatch.Value.Substring(lastOpenTagEndIndex, currentMatch.Value.LastIndexOf('}') - lastOpenTagEndIndex));
result.Append($"</{openTag}>}}[{currentMatch.Groups["code"]}]{lastOpenTagMatch.Value}");
var lastAnswerMatch = answerMatches.Last();
if (lastAnswerMatch.Index + lastAnswerMatch.Length < text.Length)
result.Append(text.Substring(lastAnswerMatch.Index + lastAnswerMatch.Length));
return result.ToString();