Как сделать сопоставление с образцом Эрланга с помощью регулярных выражений? - PullRequest
7 голосов
/ 02 ноября 2009

Когда я пишу программы на 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?) Для решения этой проблемы?

Ответы [ 6 ]

6 голосов
/ 02 ноября 2009

Можно использовать Erlang Web-style аннотации (макросы) в сочетании с модулем 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 / Аннотации

и здесь:

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

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

6 голосов
/ 02 ноября 2009

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

-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]).
3 голосов
/ 02 ноября 2009
  1. Для строки вы можете использовать модуль 're': после этого вы перебираете набор результатов. Боюсь, что нет другого способа сделать это AFAIK: вот почему есть регулярные выражения.

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

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

3 голосов
/ 02 ноября 2009

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

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

EDIT:

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)
    ...
2 голосов
/ 05 ноября 2009
  1. Эрланг не обрабатывает регулярные выражения в шаблонах.
  2. номер
1 голос
/ 02 ноября 2009

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

my_function(String) -> Matches = re:run(String, "^[A-Za-z]+[A-Za-z0-9]*$"),
                       ...
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...