Как выполнить сопоставление шаблонов Erlang с помощью регулярных выражений?

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

Например, я хотел бы сделать что-то вроде этого, где ~ - это «выдуманный» оператор сопоставления регулярных выражений:

my_function(String ~ ["^[A-Za-z]+[A-Za-z0-9]*$"]) ->
    ....

Я знаю о модуле регулярного выражения (re), но, AFAIK, вы не можете вызывать функции при сопоставлении с образцом или в охранниках.

Кроме того, я бы хотел, чтобы сопоставление строк могло выполняться без учета регистра. Это удобно, например, при разборе заголовков HTTP, я бы хотел сделать что-то вроде этого, где «Str ~ {Pattern, Options}» означает «Сопоставить Str с шаблоном с использованием параметров Options»:

handle_accept_language_header(Header ~ {"Accept-Language", [case_insensitive]}) ->
    ...

Два вопроса:

  1. Как вы обычно справляетесь с этим, используя стандартный Erlang? Есть ли какой-то механизм / стиль кодирования, который приближается к этому с точки зрения краткости и легкости чтения?

  2. Ведется ли какая-нибудь работа (EEP?) В Erlang для решения этой проблемы?


person Bruno Rijsman    schedule 02.11.2009    source источник
comment
Я сомневаюсь, что EEP будет поддерживать регулярные выражения в качестве шаблонов. Все текущие шаблоны могут быть оценены за постоянное время, регулярные выражения - нет. (длина / 1, пожалуй, единственное исключение из правила постоянного времени)   -  person archaelus    schedule 03.11.2009


Ответы (6)


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

-module(multire).

-compile(export_all).

multire([],_) ->
    nomatch;
multire([RE|RegExps],String) ->
    case re:run(String,RE,[{capture,none}]) of
    match ->
        RE;
    nomatch ->
        multire(RegExps,String)
    end.


test(Foo) ->
    test2(multire(["^Hello","world$","^....$"],Foo),Foo).

test2("^Hello",Foo) ->
    io:format("~p matched the hello pattern~n",[Foo]);
test2("world$",Foo) ->
    io:format("~p matched the world pattern~n",[Foo]);
test2("^....$",Foo) ->
    io:format("~p matched the four chars pattern~n",[Foo]);
test2(nomatch,Foo) ->
    io:format("~p failed to match~n",[Foo]).
person Rob Charlton    schedule 02.11.2009

Можно использовать аннотации (макросы) в стиле Web-стиля Erlang в сочетании с модулем re Erlang. Пример, вероятно, лучший способ проиллюстрировать это.

Вот как будет выглядеть ваш окончательный код:

[...]
?MATCH({Regexp, Options}).
foo(_Args) ->
  ok.
[...]

Макрос MATCH будет выполняться непосредственно перед вашей функцией foo. Поток выполнения завершится неудачно, если шаблон регулярного выражения не найден.

Ваша функция соответствия будет объявлена ​​следующим образом:

?BEFORE.
match({Regexp, Options}, TgtMod, TgtFun, TgtFunArgs) ->
String = proplists:get_value(string, TgtArgs),
case re:run(String, Regexp, Options) of
  nomatch ->
    {error, {TgtMod, match_error, []}};
  {match, _Captured} ->
    {proceed, TgtFunArgs}
end.

Обратите внимание:

  • BEFORE говорит, что макрос будет выполнен до вашей целевой функции (также доступен макрос AFTER).
  • Match_error - это ваш обработчик ошибок, указанный в вашем модуле, и содержит код, который вы хотите выполнить, если вы не найдете совпадения (возможно, ничего, просто заблокируйте поток выполнения)
  • Этот подход имеет то преимущество, что синтаксис и параметры регулярного выражения остаются единообразными с модулем re (избегайте путаницы).

Дополнительная информация об аннотациях Erlang Web здесь:

http://wiki.erlang-web.org/Annotations

и тут:

http://wiki.erlang-web.org/HowTo/CreateAnnotation

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

person Roberto Aloi    schedule 02.11.2009

  1. Для строки вы можете использовать модуль re: после этого вы перебираете набор результатов. Боюсь, что нет другого способа сделать это AFAIK: поэтому есть регулярные выражения.

  2. Для заголовков HTTP, поскольку их может быть много, я бы рассмотрел итерацию по набору результатов, которая была бы лучшим вариантом вместо написания очень длинного выражения (потенциально).

  3. EEP работа: не знаю.

person jldupont    schedule 02.11.2009

Вы можете использовать модуль re:

re:run(String, "^[A-Za-z]+[A-Za-z0-9]*$").
re:run(String, "^[A-Za-z]+[A-Za-z0-9]*$", [caseless]).

РЕДАКТИРОВАТЬ:

match(String, Regexps) -> 
  case lists:dropwhile(
               fun({Regexp, Opts}) -> re:run(String, Regexp, Opts) =:= nomatch;
                  (Regexp) -> re:run(String, Regexp) =:= nomatch end,
               Regexps) of
    [R|_] -> R;
    _     -> nomatch
  end.

example(String) ->
  Regexps = ["$RE1^", {"$RE2^", [caseless]}, "$RE3"]
  case match(String, Regexps) of
    nomatch -> handle_error();
    Regexp -> handle_regexp(String, Regexp)
    ...
person Zed    schedule 02.11.2009
comment
Да, модуль re отлично справляется с регулярными выражениями, но вы, AFAIK, не можете вызывать функции во время сопоставления с образцом или в охранниках. - person Bruno Rijsman; 02.11.2009
comment
Если бы я только понял, что вы имеете в виду под сопоставлением с образцом ... должен ли Erlang создать для вас регулярное выражение, которое соответствует строке, или что? - person Zed; 02.11.2009
comment
Я думаю, что ему нужно что-то вроде is_match (RegExp, S) bif для использования в охранниках, так что: foo (X) when is_match (RE1, X) - ›one_thing (); foo (X), когда is_match (RE2, X) - ›another_thing (). и Т. Д. - person Rob Charlton; 03.11.2009
comment
Хорошо, я добавил пример того, что можно было бы сделать, если это так. - person Zed; 03.11.2009

  1. Erlang не обрабатывает регулярные выражения в шаблонах.
  2. No.
person rvirding    schedule 05.11.2009

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

my_function(String) -> Matches = re:run(String, "^[A-Za-z]+[A-Za-z0-9]*$"),
                       ...
person Alexey Romanov    schedule 02.11.2009