Как настроить мое регулярное выражение для работы с многострочным и более сложным текстом? - PullRequest
1 голос
/ 21 сентября 2010

Справочная информация: я написал небольшую библиотеку, которая может создавать элементы управления asp.net из строки.

Образец текста :

Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et 
{{asp:hyperlink|NavigateUrl="/faq.aspx";Text="FAQ";}}
{{codesample|Text="FAQ";}}
accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet. Lorem ipsum dolor sit amet, consetetur 

Я получил свою первоначальную помощь здесь . Я расширил код с некоторой рефлексией, чтобы получить полную гибкость для возможности рендеринга WebControls и UserControls. Пока работает нормально, для каждого пользовательского контроля, который я тестировал. Теперь я сталкиваюсь с проблемой, заключающейся в том, что синтаксический анализ моего значения ключа свойства недостаточно гибок для поддержки произвольного мультимедийного содержимого.

Это часть кода, который я использую для строковых операций:

substring = substring.Replace("\\"", "\""); //substring is the string containing lore ipsum
substring = substring.Replace(""", "\"");
Regex r = new Regex("{{(?<single>([a-z0-9:]*))\\|((?<pair>([a-z0-9:]*=\"[a-z0-9.:/?_~=]*\";))*)}}", RegexOptions.Singleline | RegexOptions.IgnoreCase);
Match m = r.Match(substring);
if (m.Success)
{
    Dictionary<string, string> properties = new Dictionary<string, string>();
    foreach (Capture cap in m.Groups["pair"].Captures)
    {
        string key = cap.ToString().Substring(0, cap.ToString().IndexOf("="));
        if (!properties.ContainsKey(key))
        {
            string value = cap.ToString().Substring(cap.ToString().IndexOf("=\"") + 2);
            value = value.Substring(0, value.Length - 2);
            properties.Add(key, value);
        }
    }
    MethodInfo dynamicRenderControl = null;
    String controlString = m.Groups["single"].Value.ToLower();
}

(Строка взята из базы данных. Ранее она была установлена ​​в моей CMS. Я оставил наш код для получения групп {{FOO | BAR = "Foo2";}})

Вот что делает регулярное выражение: Пример:

{{asp:hyperlink|NavigateUrl="/faq.aspx";Text="FAQ";}}

Он разбирает "asp: hyperlink" на m.Groups ["single"]. Это строка, которая мне нужна для отображения на определенный тип элемента управления.

после '|' У меня есть список свойств, которые будут записаны в m.Groups ["pair"]. Captures.

Все это прекрасно работает, но не для многострочного или более сложного текста. Э.Г.

{{codesample|Text="using System.Text;<br />\r\nusing System.Bla;";}}

Здесь мой код ломается. Вопрос:
Как я должен настроить регулярное выражение, чтобы оно работало для многострочного текста, который начинается с \ "и заканчивается \"; хотя внутри этого текста также может быть \ "? Или это невозможно с регулярным выражением?

Редактировать : Я думал. Невозможно добиться того, чего я хочу с помощью регулярных выражений, потому что \ "в тексте автоматически нарушает код. Я переключаю внешний разделитель на синтаксис CDATA, который использует XML. Wikientry для CDATA

"<![CDATA[This is my content]]>";

Это означает, что каждая запись выглядит так:

{{codesample|Text="<![CDATA[this is text on the first line<br />\r\nthis is text on the second line]]>";}}

Где начало значения

"<![CDATA[

и конец

]]>";

Я пытался написать это регулярное выражение, но мне не удалось. Может ли кто-нибудь помочь мне с этим?

Ответы [ 2 ]

1 голос
/ 21 сентября 2010

Вы должны установить однострочный параметр, чтобы получить эффект, который вы описываете; Вы можете сделать это двумя способами, оба используя опцию RegexOptions.SingleLine, которая делает именно это: позволяет . также сопоставлять символы новой строки в дополнение к «любому символу».

  • в конструкторе Regex с использованием RegexOptions.SingleLine; однако это может испортить все регулярные выражения.
  • inline, используя синтаксис (?s) для его включения и (?-s) для его отключения. Вы можете использовать это, чтобы включить его непосредственно перед выражением, которое вы хотите иметь возможность сопоставлять несколько строк, и затем вернуться обратно.

Это охватывает несколько строк. Теперь для двойных кавычек, встроенных в строку ... Я предполагаю, что они каким-то образом будут экранированы? Это простой обратный слеш? Дважды процитировать? Вы должны увидеть, в чем дело, есть решение для каждого случая. Однако ... по словам какого-то очень мудрого человека (не могу вспомнить, кем он был, очевидно, мудрее меня), "если у вас возникнут проблемы и скажете - я знаю, я буду использовать регулярное выражение - теперь у вас есть два проблемы'. Это, безусловно, может иметь место, когда вы продолжаете обнаруживать угловые случаи.

Edit:

Обратите внимание, что вы можете фактически игнорировать экранированные символы ... в некоторой степени ... например, вы можете сопоставлять кавычки только тогда, когда им не предшествует обратная косая черта, с отрицательными утверждениями об обратном взгляде (я думаю, это то, что они ' как минимум) (?<!), но идти по этому пути немного сложнее. Я даже не совсем уверен, как это работает.

В случае CDATA значительно проще написать регулярное выражение; все, что вам нужно сделать, это включить одну строку, как я сказал, и:

  • соответствует началу, которое является \"\<!\[CDATA\[; вам нужно экранировать символы, потому что у большинства из них есть определенные значения в синтаксисе регулярных выражений. Чтобы быть в безопасности (если вам не нужно искать документацию о том, что именно вам нужно убежать), вы можете убежать с обратной косой чертой практически любого нестандартного символа.
  • соответствует любым символам для максимально возможной длины до следующего совпадения: (.+)? - отметьте знак вопроса, который делает совпадение не жадным.
  • соответствует конечному тегу CDATA: \]\]\>\";.

Таким образом, полное выражение будет ... (заметьте, не проверяя его):

(
{{
(?<single>\w*)
|
(?<pair>
  (?<key>\w*)="\<!\[CDATA\[ (?<cdatavalue>.*)?\]\]\>";*)
}}
)+

(я распределил его по нескольким строкам с помощью IgnoreWhitespace, чтобы сделать его более читабельным).

Однако при просмотре результатов может возникнуть неловкий код, поэтому я позволил себе немного улучшить его:

(
{{
(?<title>.*?)
\|
((?<single>\w*)
|
(?<pair>
  (?<key>\w*)
  ="\<!\[CDATA\[
  (?<cdatavalue>.+)?
  \]\]\>";
)+
)
}}
)+

(Обратите внимание, что при вставке в Visual Studio вам нужно снова экранировать кавычки!)

Что это делает, когда проходит через несколько совпадений с опцией ExplicitCapture (для захвата только именованных групп), это:

  • совпадение будет содержать группу title. Это первая часть регулярного выражения.
  • совпадение будет иметь некоторые данные в группах single или pair; Вы можете проверить с помощью string.IsNullOrEmpty, какой из них соответствует.
  • если single содержит что-то, то это то, что вы ищете.
  • если pair содержит что-то, вы можете посмотреть далее группы key и cdatavalue для пары ключ-значение, разбитой в соответствии с тем, что вы запросили.

Пример: образец текста:

{{asp:sample|test}}
{{asp:codesample|Text="<![CDATA[this is text on the first line<br />
this is text on the second line]]>";}}

Результаты:

screenshot of results in Expresso

Кроме того, не могу поверить, что я не упоминал об этом раньше: Expresso - это замечательный инструмент для тестирования и разработки регулярных выражений .net, и он бесплатный (необходимая регистрация - небольшая неприятность). 1074 *

Святая корова, это было долго. Извините за скучность.

1 голос
/ 21 сентября 2010

Если я правильно понял вашу проблему, я считаю, что это должно решить проблему?

Regex r = new Regex("{{(?<single>([a-z0-9:]*))\\|((?<pair>([a-z0-9:]*=\"[^\"]*\";))*)}}", RegexOptions.Singleline | RegexOptions.IgnoreCase);

Он фиксирует все между "и".

Br.Morten

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...