我需要一些帮助才能完成关于正则表达式的想法。
SE上有正则表达式question about better syntax,但我认为我不会使用流利的语法。 这对新手来说肯定不错,但是如果复杂的正则表达式, 你用一整页稍微好一点的胡言乱语来代替一条乱码。 我喜欢approach by Martin Fowler,其中正则表达式由较小的部分组成。 他的解决方案是可读的,但手工制作;他提出了一种聪明的方法来构建复杂的正则表达式而不是支持它的类。
我正在尝试使用类似的东西(首先参见他的例子)
final MyPattern pattern = MyPattern.builder()
.caseInsensitive()
.define("numberOfPoints", "\\d+")
.define("numberOfNights", "\\d+")
.define("hotelName", ".*")
.define(' ', "\\s+")
.build("score `numberOfPoints` for `numberOfNights` nights? at `hotelName`");
MyMatcher m = pattern.matcher("Score 400 FOR 2 nights at Minas Tirith Airport");
System.out.println(m.group("numberOfPoints")); // prints 400
其中fluent语法用于组合扩展的regex,如下所示:
`name`
创建一个命名组
`:name`
创建一个非捕获组
(?:
... )
`-name`
创建了一个反向引用
~ @#%
“)
+
或(
会非常混乱,因此不允许define('#', "\\\\")
之类的东西来匹配反斜杠可以使模式更具可读性\s
或\w
命名模式充当一种局部变量,有助于将复杂的表达式分解为小而易于理解的部分。 正确的命名模式通常不需要注释。
上述应该不难实现(我已经完成了大部分工作)并且可能非常有用,我希望如此。 你这么认为吗?
但是,我不确定它应该如何在括号内表现,有时候使用定义是有意义的,有时候不是,例如在
.define(' ', "\\s") // a blank character
.define('~', "/\**[^*]+\*/") // an inline comment (simplified)
.define("something", "[ ~\\d]")
将空间扩展到\s
是有道理的,但扩展波浪号则不然。
也许应该有一个单独的语法以某种方式定义自己的角色类?
你能想到一些命名模式非常有用或根本没用的例子吗? 我需要一些边境案例和一些改进的想法。
我看起来你不喜欢Java。我很高兴看到一些语法改进,但我无能为力。我正在寻找使用当前Java的东西。
您的示例可以使用我的语法轻松编写:
final MyPattern pattern = MyPattern.builder()
.define(" ", "") // ignore spaces
.useForBackslash('#') // (1): see (2)
.define("address", "`mailbox` | `group`")
.define("WSP", "[\u0020\u0009]")
.define("DQUOTE", "\"")
.define("CRLF", "\r\n")
.define("DIGIT", "[0-9]")
.define("ALPHA", "[A-Za-z]")
.define("NO_WS_CTL", "[\u0001-\u0008\u000b\u000c\u000e-\u001f\u007f]") // No whitespace control
...
.define("domain_literal", "`CFWS`? #[ (?: `FWS`? `dcontent`)* `FWS`? #] `CFWS1?") // (2): see (1)
...
.define("group", "`display_name` : (?:`mailbox_list` | `CFWS`)? ; `CFWS`?")
.define("angle_addr", "`CFWS`? < `addr_spec` `CFWS`?")
.define("name_addr", "`display_name`? `angle_addr`")
.define("mailbox", "`name_addr` | `addr_spec`")
.define("address", "`mailbox` | `group`")
.build("`address`");
在重写您的示例时,我遇到了以下问题:
\xdd
转义序列\udddd
必须使用好的一面: - 忽略空格没问题 - 评论没问题 - 可读性好
最重要的是: 它是普通的Java,并按原样使用现有的正则表达式引擎。
答案 0 :(得分:3)
你能想到一些命名模式非常有用或根本没用的例子吗?
在回答您的问题时,以下是命名模式特别有用的示例。它是用于解析RFC 5322邮件地址的Perl或PCRE模式。首先,凭借/x
,它处于(?x)
模式。其次,它将定义与调用分开;命名组address
是执行完整递归下降解析的东西。它的定义在非执行(?DEFINE)…)
块中跟随它。
(?x) # allow whitespace and comments
(?&address) # this is the capture we call as a "regex subroutine"
# the rest is all definitions, in a nicely BNF-style
(?(DEFINE)
(?<address> (?&mailbox) | (?&group))
(?<mailbox> (?&name_addr) | (?&addr_spec))
(?<name_addr> (?&display_name)? (?&angle_addr))
(?<angle_addr> (?&CFWS)? < (?&addr_spec) > (?&CFWS)?)
(?<group> (?&display_name) : (?:(?&mailbox_list) | (?&CFWS))? ; (?&CFWS)?)
(?<display_name> (?&phrase))
(?<mailbox_list> (?&mailbox) (?: , (?&mailbox))*)
(?<addr_spec> (?&local_part) \@ (?&domain))
(?<local_part> (?&dot_atom) | (?"ed_string))
(?<domain> (?&dot_atom) | (?&domain_literal))
(?<domain_literal> (?&CFWS)? \[ (?: (?&FWS)? (?&dcontent))* (?&FWS)?
\] (?&CFWS)?)
(?<dcontent> (?&dtext) | (?"ed_pair))
(?<dtext> (?&NO_WS_CTL) | [\x21-\x5a\x5e-\x7e])
(?<atext> (?&ALPHA) | (?&DIGIT) | [!#\$%&'*+-/=?^_`{|}~])
(?<atom> (?&CFWS)? (?&atext)+ (?&CFWS)?)
(?<dot_atom> (?&CFWS)? (?&dot_atom_text) (?&CFWS)?)
(?<dot_atom_text> (?&atext)+ (?: \. (?&atext)+)*)
(?<text> [\x01-\x09\x0b\x0c\x0e-\x7f])
(?<quoted_pair> \\ (?&text))
(?<qtext> (?&NO_WS_CTL) | [\x21\x23-\x5b\x5d-\x7e])
(?<qcontent> (?&qtext) | (?"ed_pair))
(?<quoted_string> (?&CFWS)? (?&DQUOTE) (?:(?&FWS)? (?&qcontent))*
(?&FWS)? (?&DQUOTE) (?&CFWS)?)
(?<word> (?&atom) | (?"ed_string))
(?<phrase> (?&word)+)
# Folding white space
(?<FWS> (?: (?&WSP)* (?&CRLF))? (?&WSP)+)
(?<ctext> (?&NO_WS_CTL) | [\x21-\x27\x2a-\x5b\x5d-\x7e])
(?<ccontent> (?&ctext) | (?"ed_pair) | (?&comment))
(?<comment> \( (?: (?&FWS)? (?&ccontent))* (?&FWS)? \) )
(?<CFWS> (?: (?&FWS)? (?&comment))*
(?: (?:(?&FWS)? (?&comment)) | (?&FWS)))
# No whitespace control
(?<NO_WS_CTL> [\x01-\x08\x0b\x0c\x0e-\x1f\x7f])
(?<ALPHA> [A-Za-z])
(?<DIGIT> [0-9])
(?<CRLF> \x0d \x0a)
(?<DQUOTE> ")
(?<WSP> [\x20\x09])
)
我强烈建议不要重新使用完美的车轮。从与PCRE兼容开始。如果您希望超越基本的Perl5模式,例如上面的RFC5322解析器,那么总是需要Perl6 patterns。
真的,非常支付对现有实践和文献的研究,然后开始进行开放式的研发任务。这些问题早已得到解决,有时也相当优雅。
如果你真的想要更好的Java正则表达式语法思想,你必须首先解决Java正则表达式中的这些特殊缺陷:
"foo".matches(pattern)
之类的内容以使用更好的模式库,部分原因可能不仅仅是因为final
类不可覆盖。 其中,前3个已经用几种JVM语言解决,包括Groovy和Scala;甚至Clojure都在那里。
第二组3个步骤将更加严格,但绝对是强制性的。最后一个,即使是正则表达式中最基本的Unicode支持,也只是将Java用于Unicode工作。这在游戏后期是完全不可原谅的。如果需要,我可以提供大量的例子,但你应该相信我,因为我真的知道我在这里谈论的是什么。
只有在完成所有这些工作后,您才会担心修复Java的正则表达式,以便他们能够赶上模式匹配中的当前最新技术水平。除非你照顾过去的这些疏忽,否则你不能开始关注现在,更不用说将来了。
答案 1 :(得分:1)
我认为也许正则表达式并不是真正意义上的所需,而是诸如Parser-Combinator库(可以处理字符和/或在其构造中包含正则表达式)之类的东西。 / p>
也就是说,步骤超越正则表达式的范围(不规则地实现它们 - tchrist绝对喜欢Perl实现;-)并且至少是无上下文语法 - 或者至少可以用LL(n)表示的那些,最好是最小的回溯。
Scala: The Magic Begind Parse-Combinators注意它看起来与BCNF非常相似。有一个很好的介绍。
然而,Java作为一种语言并不像某些竞争对手那样有利于无缝DSL扩展; - )