Regex обнаруживает разрыв строки внутри узла XML - PullRequest
1 голос
/ 17 декабря 2008

У меня проблемы с регулярным выражением. Я просматриваю набор файлов XML и пытаюсь обнаружить какой-то текст внутри определенных узлов, содержащих разрыв строки.

Вот некоторые примеры данных:

<item name='GenMsgText'><text>The signature will be discarded.</text></item>

<item name='GenMsgText'><text>The signature will be discarded.<break/>
Do you want to continue?</text></item>

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

if ($content =~m{<item name='GenMsgText'>(<textlist>)?<text>(.*?)</text>}si)
  {
    $t = $2;
    if ($t =~m {\n}i)
    {
     print G $t."\n\n";
    }
}

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

Ответы [ 5 ]

5 голосов
/ 17 декабря 2008

Regex не является подходящим инструментом для этой задачи, он просто не может хорошо обрабатывать вложенные структуры. Если у вас есть DOM API, этот XPath найдет нужные узлы:

Если вы ищете <break/> элементов, как показывает ваш пример:

//item[@name='GenMsgText']/text[break]

Для «настоящих» разрывов строк: CR (0xD) или LF (0xA):

//item[@name='GenMsgText']/text[contains(., '&#xD;') or contains(., '&#xA;')]
3 голосов
/ 17 декабря 2008

Я должен рассмотреть возможность использования парсера SAX для этого. Regex слишком хрупок, чтобы обрабатывать ввод XML.

0 голосов
/ 17 декабря 2008

В соответствии с тем, что упоминал Алан, вы можете использовать ленивый захват, чтобы захватывать только столько, сколько необходимо, прежде чем сопоставить заключительный текстовый оператор

<item name='GenMsgText'><text>(.*?\n.*?)</text></item>

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

0 голосов
/ 17 декабря 2008

Проблема в том, что ваш s-режим .*? может соответствовать угловым скобкам, а также символам новой строки. Если регулярное выражение начинает соответствовать элементу, который не может соответствовать, ничто не мешает ему продолжить попытку сопоставления в следующем элементе. Если вы знаете, что в тексте никогда не будет угловых скобок, вы можете ограничить совпадение одним элементом:

<item name='GenMsgText'><text>([^<>\n]*\n[^<>]*)</text></item>

РЕДАКТИРОВАТЬ: Важно отметить, что регулярные выражения, предлагаемые Максом и Кибби, должны не применяться в s-режиме (/ s, однострочный, DOTALL ...). Это то, что удерживает их от совпадения за концом элемента «item»: чтобы достичь следующего, им нужно будет сопоставить разделители строк между элементами.

Но даже без модификатора / s оба регулярных выражения могут потерпеть неудачу, если в последовательных строках есть два элемента без внутренних перевода строки (т. Е. Только с одним переводом строки между ними). Например, эти две строки будут соответствовать одной:

<item name='GenMsgText'><text>foo</text></item>
<item name='GenMsgText'><text>bar</text></item>

С другой стороны, что если в тексте более двух строк? Другие регулярные выражения соответствуют ровно одному переводу строки, поэтому они потерпят неудачу. В моем регулярном выражении я явно сопоставляю первый перевод строки, чтобы убедиться, что он есть, но если есть еще переводы строки, они будут сопоставлены вторым классом символов: [^<>]*

Именно из-за этого я стараюсь избегать использования .* или .*?.

0 голосов
/ 17 декабря 2008

Я не уверен, но думаю, что это должно работать:

<item name='GenMsgText'>(<textlist>)?<text>(.*\n.*)</text>
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...