Преобразование Markdown в алгоритм HTML вопрос для интервью — лучший подход?

#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, для создания синтаксического анализатора и сканера, это займет полчаса.