Дизайн альтернативы (быстрый?) Интерфейс для регулярных выражений

Вам действительно нужна копия Главный Первый Java

27
задан Community 23 May 2017 в 10:29
поделиться

15 ответов

Как бы вы спроектировали API?

Я бы позаимствовал страницу из API критериев Hibernate. Вместо использования:

string("a").anyTimes().or().string("b").times(2,5).compile()

Используйте такой шаблон, как:

Pattern.or(Pattern.anyTimes("a"), Pattern.times("b", 2, 5)).compile()

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

Знаете ли вы какой-либо подобный подход к регулярным выражениям?

Не навскидку, нет.

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

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

Вы бы использовали такую ​​аккуратную утилиту в своих проектах?

Возможно, поскольку она стала проверенной / стабильной ... включение его в более крупный служебный проект, такой как Apache Commons, может быть плюсом.

Как вы думаете, было бы интересно реализовать это? ;)

+1

7
ответ дан 28 November 2019 в 04:18
поделиться
6
ответ дан 28 November 2019 в 04:18
поделиться

Мартин Фаулер предлагает другую стратегию . А именно взять значимые части регулярного выражения и заменить их переменными. Он использует следующий пример:

 "^score\s+(\d+)\s+for\s+(\d+)\s+nights?\s+at\s+(.*)" 

становится

   String scoreKeyword = "^score\s+";
   String numberOfPoints = "(\d+)";
   String forKeyword = "\s+for\s+";
   String numberOfNights = "(\d+)";
   String nightsAtKeyword = "\s+nights?\s+at\s+";
   String hotelName = "(.*)";

   String pattern = scoreKeyword + numberOfPoints +
      forKeyword + numberOfNights + nightsAtKeyword + hotelName;

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

Целью предыдущего примера был синтаксический анализ numberOfPoints, numberOfNights и hotelName из списка строк, например:

score 400 for 2 nights at Minas Tirith Airport
19
ответ дан 28 November 2019 в 04:18
поделиться

Знаете ли вы какой-либо подобный подход к регулярным выражениям?

Нет, за исключением предыдущего ответа

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

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

Как бы вы спроектировали API?

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

Я думаю, что большая часть значения будет заключаться в определении надежной библиотеки служебных функций, таких как сопоставление «электронных писем», «номеров телефонов», «строк, не содержащих X» и т. Д., Которые можно настроить.

Не могли бы вы использовать такую ​​изящную утилиту в своих проектах?

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

Как вы думаете, было бы интересно реализовать это? ;)

Конечно!

1
ответ дан 28 November 2019 в 04:18
поделиться

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

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

Я бы рекомендовал аннотировать регулярное выражение следующим образом:

Pattern pattern = Pattern.compile(
  "a*     # Find 0 or more a        \n" +
  "|      # ... or ...              \n" +
  "b{2,5} # Find between 2 and 5 b  \n",
Pattern.COMMENTS);

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

Кроме того, такой инструмент, как RegexBuddy , может взять ваше регулярное выражение и преобразовать его в:

Match either the regular expression below (attempting the next alternative only if this one fails) «a*»
   Match the character "a" literally «a*»
      Between zero and unlimited times, as many times as possible, giving back as needed (greedy) «*»
Or match regular expression number 2 below (the entire match attempt fails if this one fails to match) «b{2,5}»
   Match the character "b" literally «b{2,5}»
      Between 2 and 5 times, as many times as possible, giving back as needed (greedy) «{2,5}»
]
18
ответ дан 28 November 2019 в 04:18
поделиться

A regular expression is a description of a finite state machine. The classic textual representation isn't necessarily bad. It is compact, it is relatively unambigous and it is fairly well adopted.

It MAY be that a better representation would be a state transition diagram, but that would probably be hard to use in source code.

One possibility would be to build it from a variety of container and combiner objects.

Something along the line of the following (turning this from pseudo-code to language of choice is left as an exercise for the eager):

domainlabel = oneormore(characterclass("a-zA-Z0-9-"))
separator = literal(".")
domain = sequence(oneormore(sequence(domainlabel, separator)), domainlabel)
localpart = oneormore(characterclassnot("@"))
emailaddress = sequence(localpart, literal("@"), domain)

Note that the above will incorrectly classify arbritarily many email addresses as being valid that do NOT conform to the grammar they're required to follow, as that grammar requires more than a simple FSM for full parsing. I don't believe it'd misclassify a valid address as invalid, though.

It should correspond to [^@]+@([a-zA-Z0-9-]+.)+.([a-zA-Z0-9-]+)

1
ответ дан 28 November 2019 в 04:18
поделиться

Short answer: I have seen it approached from a linting and compiling angle, which I think is something to consider for this.

Long answer: The company I work for makes hardware-based regular expression engines for enterprise content filtering applications. Think running anti-virus or firewall applications at 20GB/sec speeds right in the network routers rather than taking up valuable server or processor cycles. Most anti-virus, anti-spam or firewall apps are a bunch of regex expressions at the core.

Anyway, the way the regex's are written has a huge impact on the performance of the scanning. You can write regexs in several different ways to do the same thing, and some will have drastically faster performance. We've written compilers and linters for our customers to help them maintain and tune their expressions.

Back to the OP's question, rather than defining a whole new syntax, I would write a linter (sorry, ours is proprietary) cut and paste regex's into that will break down legacy regex's and output "fluent english" for someone to understand better. I'd also add relative performance checks and suggestions for common modifications.

4
ответ дан 28 November 2019 в 04:18
поделиться

В ответе на последнюю часть вопроса (для Kudos)

private static final String pattern = "(?:(?:\\r\\n)?[ \\t])*(?:(?:(?:[^()<>@,;:\\\".\\[\\] \\000-\\031]+(?:(?:(?:\\r\\n)?[ \\t]"
    + ")+|\\Z|(?=[\\[\"()<>@,;:\\\".\\[\\]]))|\"(?:[^\\\"\\r\\\\]|\\\\.|(?:(?:\\r\\n)?[ \\t]))*\"(?:(?:"
    + "\\r\\n)?[ \\t])*)(?:\\.(?:(?:\\r\\n)?[ \\t])*(?:[^()<>@,;:\\\".\\[\\] \\000-\\031]+(?:(?:("
    + "?:\\r\\n)?[ \\t])+|\\Z|(?=[\\[\"()<>@,;:\\\".\\[\\]]))|\"(?:[^\\\"\\r\\\\]|\\\\.|(?:(?:\\r\\n)?[ "
    + "\\t]))*\"(?:(?:\\r\\n)?[ \\t])*))*@(?:(?:\\r\\n)?[ \\t])*(?:[^()<>@,;:\\\".\\[\\] \\000-\\0"
    + "31]+(?:(?:(?:\\r\\n)?[ \\t])+|\\Z|(?=[\\[\"()<>@,;:\\\".\\[\\]]))|\\[([^\\[\\]\\r\\\\]|\\\\.)*\\"
    + "](?:(?:\\r\\n)?[ \\t])*)(?:\\.(?:(?:\\r\\n)?[ \\t])*(?:[^()<>@,;:\\\".\\[\\] \\000-\\031]+"
    + "(?:(?:(?:\\r\\n)?[ \\t])+|\\Z|(?=[\\[\"()<>@,;:\\\".\\[\\]]))|\\[([^\\[\\]\\r\\\\]|\\\\.)*\\](?:"
    + "(?:\\r\\n)?[ \\t])*))*|(?:[^()<>@,;:\\\".\\[\\] \\000-\\031]+(?:(?:(?:\\r\\n)?[ \\t])+|\\Z"
    + "|(?=[\\[\"()<>@,;:\\\".\\[\\]]))|\"(?:[^\\\"\\r\\\\]|\\\\.|(?:(?:\\r\\n)?[ \\t]))*\"(?:(?:\\r\\n)"
    + "?[ \\t])*)*\\<(?:(?:\\r\\n)?[ \\t])*(?:@(?:[^()<>@,;:\\\".\\[\\] \\000-\\031]+(?:(?:(?:\\"
    + "r\\n)?[ \\t])+|\\Z|(?=[\\[\"()<>@,;:\\\".\\[\\]]))|\\[([^\\[\\]\\r\\\\]|\\\\.)*\\](?:(?:\\r\\n)?["
    + " \\t])*)(?:\\.(?:(?:\\r\\n)?[ \\t])*(?:[^()<>@,;:\\\".\\[\\] \\000-\\031]+(?:(?:(?:\\r\\n)"
    + "?[ \\t])+|\\Z|(?=[\\[\"()<>@,;:\\\".\\[\\]]))|\\[([^\\[\\]\\r\\\\]|\\\\.)*\\](?:(?:\\r\\n)?[ \\t]"
    + ")*))*(?:,@(?:(?:\\r\\n)?[ \\t])*(?:[^()<>@,;:\\\".\\[\\] \\000-\\031]+(?:(?:(?:\\r\\n)?["
    + " \\t])+|\\Z|(?=[\\[\"()<>@,;:\\\".\\[\\]]))|\\[([^\\[\\]\\r\\\\]|\\\\.)*\\](?:(?:\\r\\n)?[ \\t])*"
    + ")(?:\\.(?:(?:\\r\\n)?[ \\t])*(?:[^()<>@,;:\\\".\\[\\] \\000-\\031]+(?:(?:(?:\\r\\n)?[ \\t]"
    + ")+|\\Z|(?=[\\[\"()<>@,;:\\\".\\[\\]]))|\\[([^\\[\\]\\r\\\\]|\\\\.)*\\](?:(?:\\r\\n)?[ \\t])*))*)"
    + "*:(?:(?:\\r\\n)?[ \\t])*)?(?:[^()<>@,;:\\\".\\[\\] \\000-\\031]+(?:(?:(?:\\r\\n)?[ \\t])+"
    + "|\\Z|(?=[\\[\"()<>@,;:\\\".\\[\\]]))|\"(?:[^\\\"\\r\\\\]|\\\\.|(?:(?:\\r\\n)?[ \\t]))*\"(?:(?:\\r"
    + "\\n)?[ \\t])*)(?:\\.(?:(?:\\r\\n)?[ \\t])*(?:[^()<>@,;:\\\".\\[\\] \\000-\\031]+(?:(?:(?:"
    + "\\r\\n)?[ \\t])+|\\Z|(?=[\\[\"()<>@,;:\\\".\\[\\]]))|\"(?:[^\\\"\\r\\\\]|\\\\.|(?:(?:\\r\\n)?[ \\t"
    + "]))*\"(?:(?:\\r\\n)?[ \\t])*))*@(?:(?:\\r\\n)?[ \\t])*(?:[^()<>@,;:\\\".\\[\\] \\000-\\031"
    + "]+(?:(?:(?:\\r\\n)?[ \\t])+|\\Z|(?=[\\[\"()<>@,;:\\\".\\[\\]]))|\\[([^\\[\\]\\r\\\\]|\\\\.)*\\]("
    + "?:(?:\\r\\n)?[ \\t])*)(?:\\.(?:(?:\\r\\n)?[ \\t])*(?:[^()<>@,;:\\\".\\[\\] \\000-\\031]+(?"
    + ":(?:(?:\\r\\n)?[ \\t])+|\\Z|(?=[\\[\"()<>@,;:\\\".\\[\\]]))|\\[([^\\[\\]\\r\\\\]|\\\\.)*\\](?:(?"
    + ":\\r\\n)?[ \\t])*))*\\>(?:(?:\\r\\n)?[ \\t])*)|(?:[^()<>@,;:\\\".\\[\\] \\000-\\031]+(?:(?"
    + ":(?:\\r\\n)?[ \\t])+|\\Z|(?=[\\[\"()<>@,;:\\\".\\[\\]]))|\"(?:[^\\\"\\r\\\\]|\\\\.|(?:(?:\\r\\n)?"
    + "[ \\t]))*\"(?:(?:\\r\\n)?[ \\t])*)*:(?:(?:\\r\\n)?[ \\t])*(?:(?:(?:[^()<>@,;:\\\".\\[\\] "
    + "\\000-\\031]+(?:(?:(?:\\r\\n)?[ \\t])+|\\Z|(?=[\\[\"()<>@,;:\\\".\\[\\]]))|\"(?:[^\\\"\\r\\\\]|"
    + "\\\\.|(?:(?:\\r\\n)?[ \\t]))*\"(?:(?:\\r\\n)?[ \\t])*)(?:\\.(?:(?:\\r\\n)?[ \\t])*(?:[^()<>"
    + "@,;:\\\".\\[\\] \\000-\\031]+(?:(?:(?:\\r\\n)?[ \\t])+|\\Z|(?=[\\[\"()<>@,;:\\\".\\[\\]]))|\""
    + "(?:[^\\\"\\r\\\\]|\\\\.|(?:(?:\\r\\n)?[ \\t]))*\"(?:(?:\\r\\n)?[ \\t])*))*@(?:(?:\\r\\n)?[ \\t]"
    + ")*(?:[^()<>@,;:\\\".\\[\\] \\000-\\031]+(?:(?:(?:\\r\\n)?[ \\t])+|\\Z|(?=[\\[\"()<>@,;:\\\\"
    + "\".\\[\\]]))|\\[([^\\[\\]\\r\\\\]|\\\\.)*\\](?:(?:\\r\\n)?[ \\t])*)(?:\\.(?:(?:\\r\\n)?[ \\t])*(?"
    + ":[^()<>@,;:\\\".\\[\\] \\000-\\031]+(?:(?:(?:\\r\\n)?[ \\t])+|\\Z|(?=[\\[\"()<>@,;:\\\".\\["
    + "\\]]))|\\[([^\\[\\]\\r\\\\]|\\\\.)*\\](?:(?:\\r\\n)?[ \\t])*))*|(?:[^()<>@,;:\\\".\\[\\] \\000-"
    + "\\031]+(?:(?:(?:\\r\\n)?[ \\t])+|\\Z|(?=[\\[\"()<>@,;:\\\".\\[\\]]))|\"(?:[^\\\"\\r\\\\]|\\\\.|("
    + "?:(?:\\r\\n)?[ \\t]))*\"(?:(?:\\r\\n)?[ \\t])*)*\\<(?:(?:\\r\\n)?[ \\t])*(?:@(?:[^()<>@,;"
    + ":\\\".\\[\\] \\000-\\031]+(?:(?:(?:\\r\\n)?[ \\t])+|\\Z|(?=[\\[\"()<>@,;:\\\".\\[\\]]))|\\[(["
    + "^\\[\\]\\r\\\\]|\\\\.)*\\](?:(?:\\r\\n)?[ \\t])*)(?:\\.(?:(?:\\r\\n)?[ \\t])*(?:[^()<>@,;:\\\""
    + ".\\[\\] \\000-\\031]+(?:(?:(?:\\r\\n)?[ \\t])+|\\Z|(?=[\\[\"()<>@,;:\\\".\\[\\]]))|\\[([^\\[\\"
    + "]\\r\\\\]|\\\\.)*\\](?:(?:\\r\\n)?[ \\t])*))*(?:,@(?:(?:\\r\\n)?[ \\t])*(?:[^()<>@,;:\\\".\\"
    + "[\\] \\000-\\031]+(?:(?:(?:\\r\\n)?[ \\t])+|\\Z|(?=[\\[\"()<>@,;:\\\".\\[\\]]))|\\[([^\\[\\]\\"
    + "r\\\\]|\\\\.)*\\](?:(?:\\r\\n)?[ \\t])*)(?:\\.(?:(?:\\r\\n)?[ \\t])*(?:[^()<>@,;:\\\".\\[\\] "
    + "\\000-\\031]+(?:(?:(?:\\r\\n)?[ \\t])+|\\Z|(?=[\\[\"()<>@,;:\\\".\\[\\]]))|\\[([^\\[\\]\\r\\\\]"
    + "|\\\\.)*\\](?:(?:\\r\\n)?[ \\t])*))*)*:(?:(?:\\r\\n)?[ \\t])*)?(?:[^()<>@,;:\\\".\\[\\] \\0"
    + "00-\\031]+(?:(?:(?:\\r\\n)?[ \\t])+|\\Z|(?=[\\[\"()<>@,;:\\\".\\[\\]]))|\"(?:[^\\\"\\r\\\\]|\\\\"
    + ".|(?:(?:\\r\\n)?[ \\t]))*\"(?:(?:\\r\\n)?[ \\t])*)(?:\\.(?:(?:\\r\\n)?[ \\t])*(?:[^()<>@,"
    + ";:\\\".\\[\\] \\000-\\031]+(?:(?:(?:\\r\\n)?[ \\t])+|\\Z|(?=[\\[\"()<>@,;:\\\".\\[\\]]))|\"(?"
    + ":[^\\\"\\r\\\\]|\\\\.|(?:(?:\\r\\n)?[ \\t]))*\"(?:(?:\\r\\n)?[ \\t])*))*@(?:(?:\\r\\n)?[ \\t])*"
    + "(?:[^()<>@,;:\\\".\\[\\] \\000-\\031]+(?:(?:(?:\\r\\n)?[ \\t])+|\\Z|(?=[\\[\"()<>@,;:\\\"."
    + "\\[\\]]))|\\[([^\\[\\]\\r\\\\]|\\\\.)*\\](?:(?:\\r\\n)?[ \\t])*)(?:\\.(?:(?:\\r\\n)?[ \\t])*(?:["
    + "^()<>@,;:\\\".\\[\\] \\000-\\031]+(?:(?:(?:\\r\\n)?[ \\t])+|\\Z|(?=[\\[\"()<>@,;:\\\".\\[\\]"
    + "]))|\\[([^\\[\\]\\r\\\\]|\\\\.)*\\](?:(?:\\r\\n)?[ \\t])*))*\\>(?:(?:\\r\\n)?[ \\t])*)(?:,\\s*("
    + "?:(?:[^()<>@,;:\\\".\\[\\] \\000-\\031]+(?:(?:(?:\\r\\n)?[ \\t])+|\\Z|(?=[\\[\"()<>@,;:\\\\"
    + "\".\\[\\]]))|\"(?:[^\\\"\\r\\\\]|\\\\.|(?:(?:\\r\\n)?[ \\t]))*\"(?:(?:\\r\\n)?[ \\t])*)(?:\\.(?:("
    + "?:\\r\\n)?[ \\t])*(?:[^()<>@,;:\\\".\\[\\] \\000-\\031]+(?:(?:(?:\\r\\n)?[ \\t])+|\\Z|(?=["
    + "\\[\"()<>@,;:\\\".\\[\\]]))|\"(?:[^\\\"\\r\\\\]|\\\\.|(?:(?:\\r\\n)?[ \\t]))*\"(?:(?:\\r\\n)?[ \\t"
    + "])*))*@(?:(?:\\r\\n)?[ \\t])*(?:[^()<>@,;:\\\".\\[\\] \\000-\\031]+(?:(?:(?:\\r\\n)?[ \\t"
    + "])+|\\Z|(?=[\\[\"()<>@,;:\\\".\\[\\]]))|\\[([^\\[\\]\\r\\\\]|\\\\.)*\\](?:(?:\\r\\n)?[ \\t])*)(?"
    + ":\\.(?:(?:\\r\\n)?[ \\t])*(?:[^()<>@,;:\\\".\\[\\] \\000-\\031]+(?:(?:(?:\\r\\n)?[ \\t])+|"
    + "\\Z|(?=[\\[\"()<>@,;:\\\".\\[\\]]))|\\[([^\\[\\]\\r\\\\]|\\\\.)*\\](?:(?:\\r\\n)?[ \\t])*))*|(?:"
    + "[^()<>@,;:\\\".\\[\\] \\000-\\031]+(?:(?:(?:\\r\\n)?[ \\t])+|\\Z|(?=[\\[\"()<>@,;:\\\".\\[\\"
    + "]]))|\"(?:[^\\\"\\r\\\\]|\\\\.|(?:(?:\\r\\n)?[ \\t]))*\"(?:(?:\\r\\n)?[ \\t])*)*\\<(?:(?:\\r\\n)"
    + "?[ \\t])*(?:@(?:[^()<>@,;:\\\".\\[\\] \\000-\\031]+(?:(?:(?:\\r\\n)?[ \\t])+|\\Z|(?=[\\[\""
    + "()<>@,;:\\\".\\[\\]]))|\\[([^\\[\\]\\r\\\\]|\\\\.)*\\](?:(?:\\r\\n)?[ \\t])*)(?:\\.(?:(?:\\r\\n)"
    + "?[ \\t])*(?:[^()<>@,;:\\\".\\[\\] \\000-\\031]+(?:(?:(?:\\r\\n)?[ \\t])+|\\Z|(?=[\\[\"()<>"
    + "@,;:\\\".\\[\\]]))|\\[([^\\[\\]\\r\\\\]|\\\\.)*\\](?:(?:\\r\\n)?[ \\t])*))*(?:,@(?:(?:\\r\\n)?["
    + " \\t])*(?:[^()<>@,;:\\\".\\[\\] \\000-\\031]+(?:(?:(?:\\r\\n)?[ \\t])+|\\Z|(?=[\\[\"()<>@,"
    + ";:\\\".\\[\\]]))|\\[([^\\[\\]\\r\\\\]|\\\\.)*\\](?:(?:\\r\\n)?[ \\t])*)(?:\\.(?:(?:\\r\\n)?[ \\t]"
    + ")*(?:[^()<>@,;:\\\".\\[\\] \\000-\\031]+(?:(?:(?:\\r\\n)?[ \\t])+|\\Z|(?=[\\[\"()<>@,;:\\\\"
    + "\".\\[\\]]))|\\[([^\\[\\]\\r\\\\]|\\\\.)*\\](?:(?:\\r\\n)?[ \\t])*))*)*:(?:(?:\\r\\n)?[ \\t])*)?"
    + "(?:[^()<>@,;:\\\".\\[\\] \\000-\\031]+(?:(?:(?:\\r\\n)?[ \\t])+|\\Z|(?=[\\[\"()<>@,;:\\\"."
    + "\\[\\]]))|\"(?:[^\\\"\\r\\\\]|\\\\.|(?:(?:\\r\\n)?[ \\t]))*\"(?:(?:\\r\\n)?[ \\t])*)(?:\\.(?:(?:"
    + "\\r\\n)?[ \\t])*(?:[^()<>@,;:\\\".\\[\\] \\000-\\031]+(?:(?:(?:\\r\\n)?[ \\t])+|\\Z|(?=[\\["
    + "\"()<>@,;:\\\".\\[\\]]))|\"(?:[^\\\"\\r\\\\]|\\\\.|(?:(?:\\r\\n)?[ \\t]))*\"(?:(?:\\r\\n)?[ \\t])"
    + "*))*@(?:(?:\\r\\n)?[ \\t])*(?:[^()<>@,;:\\\".\\[\\] \\000-\\031]+(?:(?:(?:\\r\\n)?[ \\t])"
    + "+|\\Z|(?=[\\[\"()<>@,;:\\\".\\[\\]]))|\\[([^\\[\\]\\r\\\\]|\\\\.)*\\](?:(?:\\r\\n)?[ \\t])*)(?:\\"
    + ".(?:(?:\\r\\n)?[ \\t])*(?:[^()<>@,;:\\\".\\[\\] \\000-\\031]+(?:(?:(?:\\r\\n)?[ \\t])+|\\Z"
    + "|(?=[\\[\"()<>@,;:\\\".\\[\\]]))|\\[([^\\[\\]\\r\\\\]|\\\\.)*\\](?:(?:\\r\\n)?[ \\t])*))*\\>(?:("
    + "?:\\r\\n)?[ \\t])*))*)?;\\s*)";

соответствует адресам электронной почты, совместимым с RFC: D

1
ответ дан 28 November 2019 в 04:18
поделиться

This is an intriguing concept, but as it is presented there are a few flaws.

But first answers to the key questions asked:

Now my questions:

1. Do you know of any similar approach to regular expressions?

None that haven't been mentioned already. And those I found out about by reading the question and answers.

2. Do you agree that this approach could be better than using simple strings?

If it works as advertised, it would definitely makes things much easier to debug.

3. How would you design the API?

See my notes in the next section. I take your examples and the linked .NET library as a starting point.

4. Would you use such a neat utility in your projects?

Undecided. I have no problem working with the current version of cryptic regularly expressions. And I would need a tool to convert existing regular expressions to the fluent language version.

5. Do you think this would be fun implementing? ;)

I'd enjoy working out the higher level how, than writing the actual code. Which explains the wall of text that is this answer.


Here are a couple of problems I noticed, and the way I would deal with it.

Unclear structure.

Your example seems to create a regex by concatenating on to strings. This is not very robust. I believe that these methods should be added to String and Patern/Regex objects, because it will make implementation and code cleaner. Besides it's similar to the way Regular Expressions are classically defined.

Just because I can't see it working any other way, the rest of my annotations to the proposed scheme will assume that all methods act on and return Pattern objects.

Edit: I seem to have used the following conventions throughout. So I've clarified them and move them up here.

  • Instance methods: pattern augmentation. eg: capturing, repetition, look around assertions.

  • Operators: order of operations. alternation, concatenation

  • Constants: character classes, boundaries (inplace of \w, $, \b, etc)

How will capturing/clustering be handled?

Capturing is a huge part of regular expressions.

I see each Pattern object being stored internally as a cluster. (?: pattern) in Perl terms. Allowing for pattern tokens to easily be mixed and mingled without interfering with other pieces.

I expect capturing to be done as an instance method on a Pattern. Taking a variable to store the matching string[s] in.

pattern.capture(variable) would store pattern in variable. In the event that the capture is part of an expression to be matched multiple times, variable should contain an array of strings of all matches for pattern.

Fluent languages can be very ambiguous.

Fluent languages are not well suited to the recursive nature of regular expressions. So consideration needs to be given to the order of operations. Just chaining methods together does not allow for very complex regular expressions. Exactly the situation where such a tool would be useful.

Does

Pattern pattern = string("a").anyTimes().or().string("b").times(2,5).compile();

produce /a*|b{2,5}/ or /(a*|b){2,5}/ ?

How would such a scheme handle nested alternation? Eg: /a*|b(c|d)|e/

I see three ways of handling alternation in regular expressions

  1. As an operator: pattern1 or pattern2 => pattern # /pattern1|pattern2/
  2. As a class method: Pattern.or( pattern1, pattern2[, pattern3]*) => pattern # /pattern1|patern2|patern3|...|/
  3. As a instance method: pattern1.or(pattern2) => pattern # /pattern1|patern2/

I would handle concatenation the same way.

  1. As an operator: pattern1 + pattern2 => pattern # /pattern1pattern2/
  2. As a class method: Pattern.concatenate( pattern1, pattern2[, pattern3]*) => pattern # /pattern1patern2patern3.../
  3. As a instance method: pattern1.then(pattern2) => pattern # /pattern1patern2/

How to extend for commonly used patterns

The proposed scheme used .domain() which seems to be a common regex. To treat user defined patterns as a methods doesn't make it easy to add new patterns. In a language like Java users of the library would have to override the class to add methods for commonly used patterns.

This is addressed by my suggestion to treat every piece as an object. A pattern object can be created for each commonly used regex like matching a domain for example. Given my previous thoughts about capturing it wouldn't too be hard to ensure that capturing works for multiple copies of the same common pattern that contains a captured section.

There should also be constants for patterns matching various character classes.

Zero width look around assertions

Expanding on my thoughts that all pieces should be implicitly clustered. Look around assertions shouldn't be too hard too do with an instance method.

pattern.zeroWidthLookBehind() would produce (?.


Things that still need to be considered.

  • Backreferences: Hopefully not too hard with the named capturing discussed earlier
  • How to actually implement it. I haven't given the internals too much thought. It's where the real magic is going to happen.
  • Translation: There really should be a tool translate to and from classic regexs (say Perl dialect) and the new scheme. Translating from the new scheme could be a part of the package

Putting it all together, my proposed version of a pattern that matches an email address:

Pattern domain_label = LETTER_CHARACTER + (LETTER_CHARACTER or "-" or DIGIT_CHARACTER).anyTimes()
Pattern domain = domain_label + ("." + domain_label).anyTimes()
Pattern pattern = (LETTER_CHARACTER + ALPHANUMERIC_CHARACTER + "@" + domain).compile

In hindsight, my scheme borrows heavily from Martin Fowler's approach in use. Although I didn't intend for things to go this way, it definitely makes using such a system more maintainable. It also addresses a problem or two with Fowler's approach (capturing order).

10
ответ дан 28 November 2019 в 04:18
поделиться

Я не уверен, что замена регулярного выражения на свободный API принесет много пользы.

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

Свободный API сделает любое регулярное выражение средней сложности (скажем, ~ 50 символов) даже более сложным, чем требуется, и в конечном итоге не станет легче читать, хотя это может улучшить создание регулярного выражения в среде IDE благодаря автозавершению кода. Но обслуживание кода обычно требует более высоких затрат, чем разработка кода.

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

Вы упомянули пример регулярного выражения для RFC. Я на 99% уверен (все еще есть надежда на 1% ;-)), что любой API не сделает этот пример проще, а наоборот, это только усложнит его чтение! Это типичный пример, когда вы все равно не хотите использовать регулярное выражение!

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

Не заблуждайтесь, я люблю плавные интерфейсы; Я разработал несколько библиотек, которые их используют, и использую несколько сторонних библиотек на их основе (например, FEST для тестирования Java). Но я не думаю, что они могут быть золотым молотком для любой проблемы.

Если рассматривать исключительно Java, Я думаю, что основная проблема с регулярными выражениями - это необходимое экранирование обратных косых черт в строковых константах Java. Это один момент, который делает невероятно сложным создание и понимание регулярных выражений в Java. Следовательно, первым шагом к улучшению регулярного выражения Java для меня было бы изменение языка a la Groovy , где строковым константам не нужно избегать обратных косых черт.

Пока что это было бы моим единственным предложение по улучшению регулярного выражения в Java.

0
ответ дан 28 November 2019 в 04:18
поделиться

Я говорю, действуйте, я уверен, что это интересно реализовать.

Я предлагаю использовать модель запроса (похожую на jQuery, django ORM), где каждая функция возвращает объект запроса , так что вы можете связать их вместе.

any("a").some("b").one("@").some(chars).one(".").some(chars) //a*b+@\w+\.\w+

где символы предопределены для размещения любого символа.

или могут быть достигнуты с помощью выбора:

any("a").choice("x", "z") // a(x|z)

Аргумент каждой функции может быть строка или другой запрос. Например, переменная chars , упомянутая выше, может быть определена как запрос:

//this one is ascii only
chars = raw("a-zA-Z0-9")

Итак, у вас может быть «сырая» функция, которая принимает строку регулярного выражения в качестве входных данных, если использование параметра свободная система запросов.

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

any("a") ---> "a*"
raw("b+") ----> "b+"
one(".") ---> "\."
choice("a", "b") ----> (a|b)
0
ответ дан 28 November 2019 в 04:18
поделиться

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

1
ответ дан 28 November 2019 в 04:18
поделиться

4. Вы бы использовали такую ​​полезную утилиту в своих проектах?

Скорее всего, нет. Я думаю, что это случай использования правильного инструмента для работы. Здесь есть несколько отличных ответов, например: 1579202 от Джереми Стейна. Я недавно «получил» регулярные выражения в одном из недавних проектов и обнаружил, что они очень полезны в том виде, в каком они есть, и при правильном комментировании их можно понять, если вы знаете синтаксис.

Я думаю, что часть «знание синтаксиса» - это что отталкивает людей от регулярных выражений тем, кто не понимает, что они выглядят загадочно и загадочно, но они являются мощным инструментом и в прикладной информатике (например, написание программного обеспечения для заработка) Я чувствую, что умные профессионалы должны и должны уметь научиться их использовать и правильно использовать.

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

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

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

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

1
ответ дан 28 November 2019 в 04:18
поделиться

Давайте сравним: я часто работал с (N) запросами Hibernate ICriteria, которые можно рассматривать как отображение Fluent в SQL. Я был (и до сих пор остаюсь) в восторге от них, но сделали ли они SQL-запросы более разборчивыми? Нет, скорее наоборот, но появилось другое преимущество: стало намного проще программно строить операторы, создавать их подклассы и создавать свои собственные абстракции и т. Д.

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

Чтобы добавить к вашему списку возможных альтернативных подходов и исключить их из контекста только Java, рассмотрите синтаксис LINQ. Вот как это может выглядеть (немного надумано) ( из , , где и select - ключевые слова в LINQ):

// for ^str(aa|bb){3}
from part in mystring
where part startswith "str"
and part hasgroups "aa" or "bb" as first    /* "aa" or "bb" in group 'first' */
and part repeats first 3                    /* repeat group 'first' 3 times */
select part + "extra"                       /* can contain complete statement block */

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

s как это могло бы выглядеть (немного надумано) ( из , , где и select - ключевые слова в LINQ):

// for ^str(aa|bb){3}
from part in mystring
where part startswith "str"
and part hasgroups "aa" or "bb" as first    /* "aa" or "bb" in group 'first' */
and part repeats first 3                    /* repeat group 'first' 3 times */
select part + "extra"                       /* can contain complete statement block */

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

как это могло бы выглядеть (немного надумано) ( из , , где и select - ключевые слова в LINQ):

// for ^str(aa|bb){3}
from part in mystring
where part startswith "str"
and part hasgroups "aa" or "bb" as first    /* "aa" or "bb" in group 'first' */
and part repeats first 3                    /* repeat group 'first' 3 times */
select part + "extra"                       /* can contain complete statement block */

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

0
ответ дан 28 November 2019 в 04:18
поделиться

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

Честно говоря, любой свободный интерфейс кажется сложнее для чтения, чем стандартное регулярное выражение. Для действительно коротких выражений беглая версия многословна, но не слишком длинна; это читабельно. Но то же самое и с регулярным выражением для чего-то такого длинного.

Для регулярного выражения среднего размера свободный интерфейс становится громоздким; достаточно долго, чтобы его было трудно, если не невозможно, прочитать.

Для длинного регулярного выражения (например, адреса электронной почты), где регулярное выражение на самом деле трудно (если не невозможно) прочитать,

4
ответ дан 28 November 2019 в 04:18
поделиться
Другие вопросы по тегам:

Похожие вопросы: