Perl RegEx: ограничение шаблона только первым появлением символа - PullRequest
1 голос
/ 27 июля 2010

Я пытаюсь извлечь содержимое элемента даты из многих плохо сформированных документов sgml.Например, документ может содержать простой элемент даты, такой как

<DATE>4th July 1936</DATE>

или

<DATE blaAttrib="89787adjd98d9">4th July 1936</DATE>

, но также может быть волосатым, как:

<DATE blaAttrib="89787adjd98d9">4th July 1936
<EM>spanned across multiple lines and EM element inside DATE</EM></DATE>

Цельчтобы получить "4 июля 1936 года".Поскольку файлы не большие, я решил прочитать все содержимое в переменную и выполнить регулярное выражение.Ниже приведен фрагмент моего кода Perl:

{
    local $/ = undef;
    open FILE, "$file" or die "Couldn't open file: $!";
    $fileContent = <FILE>;
    close FILE;

    if ( $fileContent =~ m/<DATE(.*)>(.*)<\/DATE>/)
    {
        # $2 should contain the "4th July 1936" but it did not.
    }
}

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

Может ли какая-нибудь добрая душа дать мне несколько указателей, указаний или подсказок?

Спасибо, куча!

Ответы [ 7 ]

3 голосов
/ 27 июля 2010

вместо совпадения . *, вы должны сопоставить «все, что не является якорем»

т.е.:


 if($string =~ /^<DATE[^>]*>([^<]+)</){

там, $ 1 - ваша дата

3 голосов
/ 27 июля 2010

Если формат даты фиксированный, вы можете использовать что-то вроде этого:

m/<DATE(.*)>([0-9]+(st|nd|rd|th)\s(January|February|March|April|May|June|July|August|September|October|November|December)\s[0-9]+)(.*)<\/DATE>/
3 голосов
/ 27 июля 2010

Использовать анализатор HTML.

Использовать анализатор HTML.

Пожалуйста, используйте парсер HTML.

Но для регулярного выражения я бы попробовал

<DATE(.*?)>(.*)<\/DATE>

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

3 голосов
/ 27 июля 2010

Используйте синтаксический анализатор XML, если можете .

Но из вашего примера, вероятно, вы могли бы попробовать

if ($fileContent =~ m/<DATE[^>]*>([^<]+)/) {
  # use $1 here
  # you may need to strip new lines
}
2 голосов
/ 27 июля 2010

Вы должны использовать не жадное сопоставление и модификатор s, чтобы сделать. соответствие новой строки

my @l = (
'<DATE>4th July 1936</DATE>',
'<DATE blaAttrib="89787adjd98d9">4th July 1936</DATE>',
'<DATE blaAttrib="89787adjd98d9">4th July 1936
<EM>spanned across multiple lines and EM element inside DATE</EM></DATE>'
);

foreach(@l) {
  /^<DATE.*?>(.*?)</s && print $1;
}

выход:

4th July 1936
4th July 1936
4th July 1936
0 голосов
/ 27 июля 2010

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

$fileContent =~ m/<DATE([^>]*)>\s*(\d+\p{Alpha}+\s+\p{Alpha}+\s+\d{4})/

всегда будет работать.(Если вы не собираетесь находить '>' в теге, то лучше не вызывать столько возвратов после того, как .* съест всю вашу строку, приведет к сбою выражения, а затем придется вернуть и проверить, верни и проверь, ...)

0 голосов
/ 27 июля 2010

Нет никакого способа использовать регулярные выражения в нескольких строках, но вы можете использовать небольшой трюк.Если файлы не слишком большие, как вы упомянули, вы можете сначала заменить все символы \ n на какое-либо значение (NEW_LINE или что-то в этом роде) или удалить их, а затем использовать свой шаблон.

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