更好的正则表达式语法思想
我需要一些帮助来完成关于正则表达式的想法。
介绍
在SE上有一个关于更好的正则表达式语法的问题,但我不认为我会使用流利的语法。 对于新手来说,它确实很好,但是如果是复杂的正则表达式,你可以用一整页稍微好一点的乱码代替一行乱码。 我喜欢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
其中使用流畅的语法来组合正则表达式,如下所示:
`name`
创建一个命名组 `:name`
创建一个非捕获组 (?:
... )
`-name`
创建一个反向引用 ~ @#%
“) +
或(
会非常混乱,所以不允许 define('#', "\")
的匹配反斜杠可以使模式更具可读性 s
或w
命名模式作为一种局部变量,有助于将复杂的表达式分解为小而易于理解的部分。 一个适当的命名模式通常不需要评论。
问题
上述应该不难实现(我已经完成了大部分工作),并且可能会非常有用,我希望。 你是这么认为的吗?
但是,我不确定它应该如何在括号内表现出来,有时候使用这些定义有时候是有意义的,有时候不会,例如在
.define(' ', "s") // a blank character
.define('~', "/**[^*]+*/") // an inline comment (simplified)
.define("something", "[ ~d]")
把空间扩大到s
是有道理的,但是扩大代字号不会。 也许应该有一个单独的语法来以某种方式定义自己的字符类?
你能想出一些例子,其中指定的模式非常有用或根本没用吗? 我需要一些边界案例和一些改进想法。
对tchrist的回答作出反应
评论他的反对意见
我看起来像你不喜欢Java。 我很高兴看到这里有一些语法改进,但我无能为力。 我正在寻找与当前Java一起工作的东西。
RFC 5322
您的示例可以使用我的语法轻松编写:
final MyPattern pattern = MyPattern.builder()
.define(" ", "") // ignore spaces
.useForBackslash('#') // (1): see (2)
.define("address", "`mailbox` | `group`")
.define("WSP", "[u0020u0009]")
.define("DQUOTE", """)
.define("CRLF", "rn")
.define("DIGIT", "[0-9]")
.define("ALPHA", "[A-Za-z]")
.define("NO_WS_CTL", "[u0001-u0008u000bu000cu000e-u001fu007f]") // 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
必须使用udddd
在光明的一面: - 忽略空间是没有问题的 - 评论是没有问题的 - 可读性很好
最重要的是: 它是普通的Java,并按原样使用现有的正则表达式引擎。
命名捕捉示例
你能想出一些例子,其中指定的模式非常有用或根本没用吗?
回答你的问题,下面是一个例子,其中命名模式特别有用。 这是用于解析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-x5ax5e-x7e])
(?<atext> (?&ALPHA) | (?&DIGIT) | [!#$%&'*+-/=?^_`{|}~])
(?<atom> (?&CFWS)? (?&atext)+ (?&CFWS)?)
(?<dot_atom> (?&CFWS)? (?&dot_atom_text) (?&CFWS)?)
(?<dot_atom_text> (?&atext)+ (?: . (?&atext)+)*)
(?<text> [x01-x09x0bx0cx0e-x7f])
(?<quoted_pair> (?&text))
(?<qtext> (?&NO_WS_CTL) | [x21x23-x5bx5d-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-x27x2a-x5bx5d-x7e])
(?<ccontent> (?&ctext) | (?"ed_pair) | (?&comment))
(?<comment> ( (?: (?&FWS)? (?&ccontent))* (?&FWS)? ) )
(?<CFWS> (?: (?&FWS)? (?&comment))*
(?: (?:(?&FWS)? (?&comment)) | (?&FWS)))
# No whitespace control
(?<NO_WS_CTL> [x01-x08x0bx0cx0e-x1fx7f])
(?<ALPHA> [A-Za-z])
(?<DIGIT> [0-9])
(?<CRLF> x0d x0a)
(?<DQUOTE> ")
(?<WSP> [x20x09])
)
我强烈建议不要再找一辆完美的车轮。 开始与PCRE兼容。 如果你希望超越基本的Perl5模式,比如上面的RFC5322解析器,总会有Perl6模式被使用。
在开放研发任务之前,确实需要对现有的实践和文献进行研究。 这些问题早已得到解决,有时相当优雅。
改进Java正则表达式语法
如果您真的想要更好的Java语法正则表达式,您必须首先解决Java正则表达式中的这些特定缺陷:
"foo".matches(pattern)
来使用更好的模式库,部分但不仅仅因为final
类不可覆盖。 其中,前3个已经用几种JVM语言解决,包括Groovy和Scala; 即使是Clojure也在那里兼职。
第二组步骤将更加艰难,但绝对是强制性的。 最后一个,在正则表达式中甚至没有最基本的Unicode支持,只是简单地杀死了Java的Unicode工作。 这在游戏的后期是完全不可原谅的。 如果需要,我可以提供大量示例,但您应该相信我,因为我确实知道我在这里谈论的是什么。
只有一旦你完成了所有这些,你应该担心修正Java的正则表达式,以便它们能够赶上模式匹配领域的当前状态。 除非你照顾过去的疏忽,否则你不能开始看待现在,更不用说未来。
我认为也许正则表达式并不是真正所需的东西,而是诸如Parser-Combinator库(可以在字符上工作和/或在其构造中包含正则表达式)的东西。
也就是说,超越正则表达式的范围(像它们可能被实现的那样不规则 - tchrist肯定享有Perl实现;-)并且进入上下文无关语法 - 或者至少可以用LL(n)表示的那些语法,优选以最小的回溯。
Scala:Magic Begind Parse-Combinators注意它看起来与BCNF非常相似。 有一个很好的介绍。
Haskel:Parsec Ditto。
Java中的一些例子是JParsec和JPC。
然而,Java作为一种语言并不像一些竞争者那样有利于这种无缝的DSL扩展;-)
链接地址: http://www.djcxy.com/p/50715.html上一篇: Better regex syntax ideas
下一篇: pgadmin plugins?