#algorithm #text-processing #text-parsing #converters
#алгоритм #обработка текста #синтаксический анализ текста #конвертеры
Вопрос:
Недавно я получил этот вопрос на техническом собеседовании, и у меня не хватило времени.
Задача состояла в том, чтобы написать конвертер Markdown в HTML. Учитывая следующие входные данные:
This is a paragraph with a soft
line break.
This is another paragraph that has
> Some text that
> is in a
> block quote.
This is another paragraph with a ~~strikethrough~~ word.
Создайте следующий вывод:
<p>This is a paragraph with a soft<br />line break.</p>
<p>This is another paragraph that has <br />
<blockquote>Some text that<br />is in a<br />block quote.</blockquote>
</p>
<p>This is another paragraph with a <del>strikethrough</del> word.</p>
Форматирование выходных данных не важно, просто должно быть допустимым HTML.
Я не могу придумать хороший подход для этого. Единственное, что я могу придумать, это разделить на двойные новые строки, чтобы получить абзацы, а затем выполнить итерацию по каждому абзацу, чтобы проверить наличие каждого специального символа и заменить его эквивалентом HTML. Хотя это кажется хакерским, и я считаю, что существует более структурированный способ решения этого класса проблем, учитывая, что это собеседование с ограничением по времени примерно на 30 минут.
Я приветствую любые предложения.
Ответ №1:
Это моя попытка за один час c #.
Код неоднократно пытается объединить соседние строки, если это возможно. Требуется дополнительное время, чтобы настроить это для удобства чтения и производительности. Некоторая дополнительная проверка на ошибки была бы не помешала.
using System;
using System.Collections.Generic;
namespace akMdConverter
{
class Program
{
const string Break = "<br />";
static void Main(string[] args)
{
var md = new string[]
{
"This is a paragraph with a soft",
"line break.",
"",
"This is another paragraph that has",
"> Some text that",
"> is in a",
"> block quote.",
"",
"This is another paragraph with a ~~strikethrough~~ word."
};
md = MergeBlockQuotes(md);
md = MergeSoftLineBreaks(md);
md = MergeParagraphs(md);
ReplaceStrikeThroughs(md);
foreach (var m in md)
{
Console.WriteLine(m);
}
}
static string[] MergeBlockQuotes(string[] md)
{
var lines = new List<string>();
string previousLine = "";
foreach(string line in md)
{
if (line.StartsWith(">"))
{
if (previousLine != "")
{
previousLine = previousLine Break
line.Substring(1).Trim();
}
else
{
previousLine = line.Trim();
}
}
else
{
if (previousLine != "")
{
lines.Add(previousLine);
previousLine = "";
}
lines.Add(line);
}
}
return lines.ToArray();
}
static string[] MergeSoftLineBreaks(string[] md)
{
var lines = new List<string>();
string previousLine = "";
foreach (string line in md)
{
if ((line == "") || line.StartsWith(">"))
{
if (previousLine != "")
{
lines.Add(previousLine);
lines.Add(line);
previousLine = "";
}
else
{
lines.Add(line);
}
}
else
{
if (previousLine != "")
{
previousLine = Break;
}
previousLine = line;
}
}
if (previousLine != "")
{
lines.Add(previousLine);
}
return lines.ToArray();
}
static string[] MergeParagraphs(string[] md)
{
var lines = new List<string>();
string previousLine = "";
foreach (string line in md)
{
if (line == "")
{
if (previousLine != "")
{
lines.Add(Bracket(previousLine, "p"));
previousLine = "";
}
lines.Add("");
}
else if (line.StartsWith(">"))
{
if (previousLine != "")
{
previousLine = Break "n";
}
previousLine = Bracket(line.Substring(1), "blockquote");
}
else
{
if (previousLine != "")
{
previousLine = Break;
}
previousLine = line;
}
}
if (previousLine != "")
{
lines.Add(Bracket(previousLine, "p"));
}
return lines.ToArray();
}
static string Bracket(string s, string bracket)
{
return "<" bracket ">" s.Trim() "<" bracket "/>";
}
static void ReplaceStrikeThroughs(string[] md)
{
for(int i = 0; i < md.Length; i )
{
string s = md[i];
int tilde1 = s.IndexOf("~~");
while (tilde1 >= 0)
{
int tilde2 = s.IndexOf("~~", tilde1 2);
if (tilde2 > tilde1)
{
s = s.Substring(0, tilde1) "<del>"
s.Substring(tilde1 2, tilde2 - tilde1 - 2) "</del>"
s.Substring(tilde2 2);
tilde1 = s.IndexOf("~~");
}
else
{
tilde1 = -1;
}
}
md[i] = s;
}
}
}
}
Комментарии:
1. Спасибо, Аксель, что нашел время показать, как это сделать. Как вы думаете, смогли бы вы написать это за 30 минут, в обстановке интервью? Я нашел одну библиотеку Python, которая преобразует Markdown в HTML, и просмотрел исходный код — в нем повсюду использовались регулярные выражения.
2. Нет, я не смог бы запрограммировать его за полчаса. Возможно, можно было бы набросать грамматику для этого подмножества markdown и написать небольшой анализатор рекурсивного спуска. Если вам разрешено использовать такие инструменты, как Bison и Flex, для создания синтаксического анализатора и сканера, это займет полчаса.