2011-01-08 2 views
1

Я просто написал простой класс. Все это делает принимает строку ввода, какКак улучшить этот простой класс рендеринга шаблонов?

Здравствуйте @name, вы смотрите @adjective сегодня!

И заменяет @variables своими значениями из словаря. Например, проходящий в new Dictionary<string,object>{{"name","Ralph"},{"adjective","stunning"}} даст:

Здравствуйте, Ральф, вы сегодня выглядите потрясающе!

Вот класс:

class Template 
{ 
    List<object> nodes = new List<object>(); 

    public Template(string content) 
    { 
     for (int i = 0; i < content.Length; ++i) 
     { 
      char ch = content[i]; 

      if (ch == '@') 
      { 
       var match = Regex.Match(content.Substring(i + 1), @"\w+"); 
       if (match.Success) 
       { 
        nodes.Add(new Variable(match.Value)); 
        i += match.Value.Length; 
       } 
       else 
       { 
        throw new Exception(string.Format("Expected variable name after @ symbol at character {0}", i)); 
       } 
      } 
      else 
      { 
       nodes.Add(ch.ToString()); 
      } 
     } 
    } 

    public string Render(Dictionary<string,object> dict) 
    { 
     var sb = new StringBuilder(); 
     foreach (var item in nodes) 
     { 
      if (item is Variable) 
      { 
       sb.Append(dict[((Variable)item).Name]); 
      } 
      else 
      { 
       sb.Append(item); 
      } 
     } 
     return sb.ToString(); 
    } 

    class Variable 
    { 
     public readonly string Name; 
     public Variable(string name) 
     { 
      Name = name; 
     } 
    } 
} 

Это хороший подход к такой проблеме? Я хочу сделать как можно больше обработки в конструкторе, чтобы я мог повторно отображать шаблон снова и снова, не переупаковывая его.

Прямо сейчас я перебираю весь список узлов, ищущих переменные узлы, чтобы их можно было заменить. Может быть, есть способ, которым я могу «пропустить» эти узлы? Это поможет?

Кроме того, я разбираю его символ по символу, но затем я использую регулярное выражение (которое я хочу начать с следующего символа, поэтому я использую .Substring, чтобы получить оставшуюся часть строки), чтобы получить «кусок» "текста. Я не уверен, как еще я могу получить полное имя переменной w/out с помощью регулярного выражения?

Этот класс собирается стать намного более сложным, поэтому я хочу убедиться, что у меня есть правильный подход, прежде чем я пойду дальше.

Мои 2 проблемы являются:

  1. Что такое хороший способ, чтобы вытащить кусок текста из текущей позиции, и продвигать счетчик? Например, извлечение имени переменной после того, как я определил, что следующий бит текста должен быть именем переменной.
  2. Как хранить узлы (в данном случае, только текстовые и переменные узлы), чтобы я мог быстро визуализировать шаблон снова и снова?
+0

Это текст, который вы попытаетесь сделать с помощью своих шаблонов? RegExes не подходят для разветвленных структур. – pbalaga

+0

@rook: Да, если под «текстом» вы подразумеваете «строки», но это будет HTML ... возможно, JSON или XML в будущем. Чтобы быть ясным, * output * будет HTML, а не входным. – mpen

ответ

2

насчет:

s = s.Replace("@name", name); 
s = s.Replace("@Adjective", adjective); 

Кроме того, я хотел бы избежать бросать исключения при разборе текста. Поскольку пользовательские строки могут содержать практически все, лучше просто попытаться обрабатывать неожиданные данные как можно разумнее.

+0

Это не просто простые переменные подстановки в будущем. Я собираюсь добавить такие вещи, как для циклов и т. Д., Регулярные выражения не будут работать. Я не думаю, что бросать исключение слишком плохо; если они неправильно написали шаблон, им нужно знать об этом. Либо это, либо он молча ничего не выплевывает, и они царапают голову, почему это неправильно. – mpen

+1

Извините, если я не совсем понимаю, что вы пытаетесь сделать. Я просто так много сделал. Если вам нужен простой класс-помощник парсера, проверьте мой класс синтаксического разбора текста (http://www.blackbeltcoder.com/Articles/strings/a-text-parsing-helper-class). –

+0

Я собираюсь использовать его для анализа этого http://programmers.stackexchange.com/questions/34761 Этот класс - только начало. Вы сказали себе, в своей статье, что нужно сканировать персонаж по-символу;) – mpen

1

Вы почти наверняка захотите использовать какой-то ассоциативный контейнер для своих узлов, могу ли я рекомендовать Dictionary<string, Node> вместо List? Кроме того, я не вижу способа установить эти узлы. И вы также можете идти вперед и делать узлы классом, пока вы на нем.

Что касается синтаксического анализа символа char, а затем с помощью регулярного выражения, да, это, вероятно, не идеально, но писать токенизатор/лексер также не является простейшим из заданий. Исправьте его, когда вам нужно!

+0

Да, я занимался ANTLR, но я еще не понял его, не слишком люблю его. Мог бы быть излишним для того, что я хочу в любом случае. Как будет помогать «Словарь »? Мне все равно нужно вытащить переменные узлы, которые все равно будут «O (n)», а словари не упорядочены, не так ли? Узлы не нужно устанавливать снова после того, как шаблон разобран, просто прочитайте и визуализируйте. – mpen

+0

Никакие словари не выполняются, по крайней мере, не в порядке вставки. Словарь является хорошим местом для хранения ключевых значений для поиска, но не для хранения списка для обработки по порядку. –

0

Вместо того, чтобы перебирать КАЖДЫЙ символ вашей строки, вы должны просто выполнить свой RegEx по всей строке и вернуть коллекцию совпадений.

+0

Нельзя просто найти и заменить. Будет больше элементов, чем просто переменных ...он будет ветвью, поэтому мне нужно обработать его линейно. – mpen

1

Если вы собираетесь использовать шаблон несколько раз, я бы предложил разбор шаблона в 2 этапа, сначала разделил шаблон в литеральном тексте и объектах поля и сохранил их в списке, а затем каждый раз, когда вам нужно визуализировать он просто перебирает список и выводит любой литеральный текст напрямую и вызывает словарь для каждого поля.

Мы сделали что-то похожее с нашим собственным движком temlpate, переходящим из регулярного выражения в предварительно разобранный список и увеличенной производительностью около 20 раз: D.

Ваш текущий код был бы идеальным, чтобы разделить исходную строку.

Пример:

List<Node> nodes = var list = new List<node> {new node {Type=ntypes.literal,Value="Hello "}, new node{Type=ntypes.field,Value="Name"}}; 

Enum NodeType { 
    field, 
    literal 
} 
class Node { 
    public enum NodeType; 
    public string Value; 
} 

Если вы будете использовать его только occationally еще можно было разделить его, но затем сохранить массив в течение eaxmple JSON или сериализовать его так, чтобы можно было легко воссоздать список без использования регулярное выражение.

Также, если в массиве содержатся элементы с (тип, значение), вы можете добавить к нему больше времени, много разных словарей или, может быть, простых функций, таких как @@ substring (@field, 10), которые затем анализируются и исполняются;)

Но для более сложных ситуаций решение для реального парсера лучше, так как с увеличением сложности все простые решения получат неудобную и жертвующую производительность.

Мы окончательно обратились к парсеру, основанному на Antlr, и в настоящее время просматриваем деревья выражений для фазы рендеринга.

+0

Ну, это именно то, что я пытался сделать. Я рад, что это хороший подход. :) Сначала я попытаюсь разобрать его самостоятельно ... тогда я могу попробовать этот генератор синтаксиса C# ... забыть, что он сейчас называется. – mpen

+0

Для простых @fieldnames ручной разветвитель достаточно хорош, первый, который мы использовали, был написан моим коллегой за 20 минут;) и был очень похож на ваш код;) –

Смежные вопросы