Бесконечный цикл RegExp только в perl, почему?

У меня есть регулярное выражение для проверки того, содержит ли ячейка CSV правильный путь к файлу:

EDIT В CSV перечислены пути к файлам, которые еще не существуют при запуске скрипта (я не могу использовать -e), а путь к файлу может включать * или %variable% или {$variable}.

my $FILENAME_REGEXP = '^(|"|""")(?:[a-zA-Z]:[\\\/])?[\\\/]{0,2}(?:(?:[\w\s\.\*-]+|\{\$\w+}|%\w+%)[\\\/]{0,2})*\1$';

Так как ячейки CSV иногда содержат обертки из двойных кавычек, а иногда и само имя файла нужно заключать в двойные кавычки, я сделал такую ​​группировку (|"|""")...\1

Затем с помощью этой функции:

sub ValidateUNCPath{
    my $input = shift;
    if ($input !~ /$FILENAME_REGEXP/){
        return;
    } 
    else{
        return "This is a Valid File Path.";
    }

}

Я пытаюсь проверить, соответствует ли эта фраза моему регулярному выражению (она не должна совпадать):

"""c:\my\dir\lord"

но мой дорогой Perl попадает в бесконечный цикл, когда:

ValidateUNCPath('"""c:\my\dir\lord"');

EDIT на самом деле он зацикливается на этом:

ValidateUNCPath('"""\aaaaaaaaa\bbbbbbb\ccccccc\Netwxn00.map"');

Я убедился в http://regexpal.com, что мое регулярное выражение правильно улавливает эти несимметричные """ ... " заворачивая в двойные кавычки, но у Перла свое мнение :(

Я даже пробовал использовать флаги /g и /o в

/$FILENAME_REGEXP/go

но все равно висит. Что мне не хватает?


person Noam Manos    schedule 10.05.2012    source источник
comment
Вы не опубликовали код, который может вызвать бесконечный цикл.   -  person TLP    schedule 10.05.2012
comment
вместо возврата; попробовать вернуться;   -  person Hitham S. AlQadheeb    schedule 10.05.2012
comment
Синтаксис if (...) return; else return; вызовет синтаксическую ошибку в perl... почему бы вам не вставить свой настоящий код, включая используемый вами цикл, тогда мы сможем вам помочь.   -  person TLP    schedule 10.05.2012
comment
Спасибо TLP - я убрал ненужные строки, теперь { } исправлен.   -  person Noam Manos    schedule 10.05.2012
comment
@NoamManos Вы еще не вставили код, показывающий бесконечный цикл.   -  person TLP    schedule 10.05.2012
comment
0_0 Почему бы не использовать Text::CSV для захвата полей в каждой строке, а затем проверить путь к файлу с помощью -e?   -  person    schedule 10.05.2012
comment
Если пути к файлам могут не существовать во время выполнения, то как узнать, что путь к файлу действителен? Другими словами, объясните, чего вы пытаетесь достичь с помощью своего регулярного выражения.   -  person    schedule 10.05.2012
comment
@Ноам Манос - он бесконечно отступает из-за этого [\w\s\.*-]+ избавиться от квантификатора +.   -  person    schedule 11.05.2012
comment
Должен быть лучший способ делать то, что вы хотите. Какой правильный путь? Это файлы, которые существуют, или строки - это законный путь в Windows? Поверьте, любую проблему можно решить намного проще.   -  person Øyvind Skaar    schedule 11.05.2012
comment
@Jack Maney - Text::CSV не входит в пакет Perl по умолчанию, это модуль CPAN, и мой скрипт работает на нескольких сборочных машинах, поэтому я не могу постоянно обновлять их установки Perl...   -  person Noam Manos    schedule 20.05.2012
comment
Кто сказал что-нибудь об обновлении установки Perl? Все, что вам нужно сделать, это установить модуль на каждую машину. Или, что еще проще, вы можете настроить NFS с local::lib.   -  person    schedule 20.05.2012


Ответы (4)


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

Когда я пробую вашу подпрограмму, она возвращает true для всех видов строк, которые далеки от путей, например:

.....
This is a Valid File Path.
.*.*
This is a Valid File Path.
-
This is a Valid File Path.

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

^(|"|""")                  # can match the empty string
(?:[a-zA-Z]:[\\\/])?       # same, matches 0-1 times
[\\\/]{0,2}                # same, matches 0-2 times
(?:(?:[\w\s\.\*-]+|\{\$\w+}|%\w+%)[\\\/]?)+\1$  # only this is not optional

Поскольку только последняя часть фактически должна соответствовать чему-либо, вы разрешаете все типы строк, в основном в первом классе символов: [\w\s\.\*-]

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

Почему бы вам просто не удалить кавычки? Кроме того, если этот путь существует в вашей системе, есть гораздо более простой способ проверить его правильность: -e $path

person TLP    schedule 10.05.2012
comment
Все ваши тесты имеют совершенно правильные относительные пути к файлам, поэтому они не показывают, что регулярное выражение не работает. - person Rob Kennedy; 10.05.2012
comment
@RobKennedy Я не думаю, что любая строка, состоящая из [\w\s\.\*-]+, является допустимым путем. - person TLP; 10.05.2012
comment
Каждый из ваших тестов соответствует этому регулярному выражению, и каждый из ваших тестов является допустимым относительным путем. - person Rob Kennedy; 10.05.2012
comment
@RobKennedy Как вам эти примеры? - person TLP; 10.05.2012
comment
@TLP хммм, да, скрипт должен разрешать как полные, так и относительные пути. Но я до сих пор не понимаю, почему perl вычисляет регулярное выражение целую вечность. Пожалуйста, попробуйте эту фразу - мой perl зависает на ней: ValidateUNCPath('\aaaaaaaaa\bbbbbb\ccccccc\Netwxn00.map'); - person Noam Manos; 10.05.2012
comment
@NoamManos Вау, это странно .. никогда раньше не видел, чтобы регулярное выражение зависало. У меня нет времени анализировать это сейчас, но я готов поспорить, потому что у вас есть возможность пустой строки в первой скобке и \1, вызывающая бесконечный возврат. - person TLP; 10.05.2012

Если бы механизм регулярных выражений был наивным,

('y') x 20 =~ /^.*.*.*.*.*x/

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

20 * 20 * 20 * 20 * 20 = 3,200,000 possible matches.

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

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

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

person ikegami    schedule 10.05.2012

Обновить

Изменить: из-за проб и ошибок приведенное ниже подвыражение группировки [\w\s.*-]+ вызывает проблему с возвратом

    (?:
        (?:
             [\w\s.*-]+
          |  \{\$\w+\}
          |  %\w+%
        )
        [\\\/]?
    )+

Исправление № 1, развернутый метод

'
 ^
    (                          # Nothing
      |"                       # Or, "
      |"""                     # Or, """
    )
                      # Here to end, there is no provision for quotes (")
    (?:               # If there are no balanced quotes, this will fail !!
        [a-zA-Z]
        :
        [\\\/]
    )?
    [\\\/]{0,2}

    (?:
        [\w\s.*-]
      |  \{\$\w+\}
      |  %\w+%
    )+
    (?:
        [\\\/]
        (?:
            [\w\s.*-]
          |  \{\$\w+\}
          |  %\w+%
        )+
    )*
    [\\\/]?
    \1
 $
'

Исправление № 2, независимое подвыражение

'
 ^
    (                          # Nothing
      |"                       # Or, "
      |"""                     # Or, """
    )
                      # Here to end, there is no provision for quotes (")
    (?:               # If there are no balanced quotes, this will fail !!
        [a-zA-Z]
        :
        [\\\/]
    )?
    [\\\/]{0,2}

    (?>
       (?:
           (?:
                [\w\s.*-]+
             |  \{\$\w+\}
             |  %\w+%
           )
           [\\\/]?
       )+
    )
    \1
 $
'

Исправить № 3, удалить квантификатор + (или добавить +?)

'
 ^
    (                          # Nothing
      |"                       # Or, "
      |"""                     # Or, """
    )
                      # Here to end, there is no provision for quotes (")
    (?:               # If there are no balanced quotes, this will fail !!
        [a-zA-Z]
        :
        [\\\/]
    )?
    [\\\/]{0,2}

    (?:
        (?:
             [\w\s.*-] 
          |  \{\$\w+\}
          |  %\w+%
        )
        [\\\/]?
    )+
    \1
 $
'
person Community    schedule 10.05.2012
comment
Это именно то, что я хочу - сбой на несимметричных кавычках. Но проблема в том, что Perl долго вычисляет регулярное выражение. Пожалуйста, попробуйте эту фразу - мой perl зависает на ней: **ValidateUNCPath('\aaaaaaaaa\bbbbbb\ccccccc\Netwxn00.map'); ** - person Noam Manos; 10.05.2012
comment
@Ноам Манос - Хорошо, я вижу проблему. Выложил обновление с исправлением. - person ; 10.05.2012
comment
Спасибо! вы нашли причину зацикливания, хотя я не понимаю, почему RegExPal.com работал, но Perl раньше зависал... И что делает ?› в исправлении № 2? - person Noam Manos; 11.05.2012
comment
(?>) — это атомарная группа. Это то же самое, что квантор притяжения, за исключением того, что он применяется как группа вместо квантификатора. Проблема, с которой вы столкнулись, связана с FAIL и наличием вложенных квантиферов. ТЭ: (?:(?:[]+)+. В вашем регулярном выражении было слишком много возвратных перестановок при FAIL. Узнайте о влиянии вложенных квантификаторов. Притяжательное выражение, необходимое вашему регулярному выражению, выглядит примерно так: '^(|"|""")(?:[a-zA-Z]:[\\\/])?[\\\/]{0,2}(?:(?:[\w\s.*-]++|\{\$\w+\}|%\w+%)[\\\/]?)+\1$'. Удачи! - person ; 11.05.2012

Благодаря sln это мое фиксированное регулярное выражение:

my $FILENAME_REGEXP = '^(|"|""")(?:[a-zA-Z]:[\\\/])?[\\\/]{0,2}(?:(?:[\w\s.-]++|\{\$\w+\}|%\w+%)[\\\/]{0,2})*\*?[\w.-]*\1$';

(Я также запретил * char в каталогах и разрешил только один * в (последнем) имени файла)

person Noam Manos    schedule 11.05.2012
comment
Вы должны быть в состоянии значительно ускорить это, используя '^(|"|""")(?:[a-zA-Z]:[\\\/])?[\\\/]{0,2}(?:(?:[\w\s.-]++|\{\$\w+\}|%\w+%)[\\\/]{0,2})*\*?[\w.-]*\1$'. Это объединяет перекрытие и добавляет квантор Possesive ++ (такой же, как атомарная группировка (?>)), который вызывал катастрофический возврат вложенного квантификатора в старом регулярном выражении. В самом маленьком смысле, вместо (?:(?:[\w\s.-]))*, (?:(?:[\w\s.-]++))* при FAIL произойдет немедленный сбой, а не возврат и использование быстрее, а не по 1 символу за раз. - person ; 11.05.2012
comment
Спасибо @sln - на моем локальном Perl теперь все отлично работает! (Однако Regexpal.com не принимает это....) - Я изменяю свой последний ответ, как вы предложили. - person Noam Manos; 12.05.2012