更好的正则表达式语法思想

我需要一些帮助来完成关于正则表达式的想法。

介绍

在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`创建一个命名组
  • 助记符:shell捕获反引号中的命令结果
  • `:name`创建一个非捕获组
  • 助记符:类似于(?: ... )
  • `-name`创建一个反向引用
  • 助记符:破折号将它连接到以前的事件
  • 重新定义个别角色并在任何地方使用它,除非被引用
  • 这里只允许一些字符(例如~ @#% “)
  • 重新定义+(会非常混乱,所以不允许
  • 在上面的例子中,重新定义空间意味着任何间距都是非常自然的
  • 重新定义角色可能会使图案更紧凑,除非过度使用,否则这是很好的选择
  • 例如,使用类似define('#', "\")的匹配反斜杠可以使模式更具可读性
  • 重新定义一些引用的序列,如sw
  • 标准定义不符合Unicode
  • 有时你可能会有自己的想法是什么字或空间
  • 命名模式作为一种局部变量,有助于将复杂的表达式分解为小而易于理解的部分。 一个适当的命名模式通常不需要评论。

    问题

    上述应该不难实现(我已经完成了大部分工作),并且可能会非常有用,我希望。 你是这么认为的吗?

    但是,我不确定它应该如何在括号内表现出来,有时候使用这些定义有时候是有意义的,有时候不会,例如在

    .define(' ', "s")            // a blank character
    .define('~', "/**[^*]+*/")   // an inline comment (simplified)
    .define("something", "[ ~d]")
    

    把空间扩大到s是有道理的,但是扩大代字号不会。 也许应该有一个单独的语法来以某种方式定义自己的字符类?

    你能想出一些例子,其中指定的模式非常有用或根本没用吗? 我需要一些边界案例和一些改进想法。

    对tchrist的回答作出反应

    评论他的反对意见

  • 缺少多行模式字符串。
  • Java中没有多行字符串,我想更改,但不能。
  • 免于疯狂繁琐且容易出错的双反斜杠...
  • 这又是我不能做的事情,我只能提供解决方法。 下面。
  • 缺乏无效正则表达式文本的编译时异常,以及编译时正确编译的正则表达式文本缺少缓存。
  • 由于正则表达式只是标准库的一部分,而不是语言本身的一部分,因此没有什么可以在这里完成。
  • 没有调试或分析工具。
  • 我无能为力。
  • 不符合UTS#18。
  • 按照我的建议重新定义相应的模式可以很容易地解决这个问题。 这不是完美的,因为在调试器中,你会看到被替换的炸弹。
  • 我看起来像你不喜欢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) | (?&quoted_string))
         (?<domain>          (?&dot_atom) | (?&domain_literal))
         (?<domain_literal>  (?&CFWS)? [ (?: (?&FWS)? (?&dcontent))* (?&FWS)?
                                       ] (?&CFWS)?)
         (?<dcontent>        (?&dtext) | (?&quoted_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) | (?&quoted_pair))
         (?<quoted_string>   (?&CFWS)? (?&DQUOTE) (?:(?&FWS)? (?&qcontent))*
                              (?&FWS)? (?&DQUOTE) (?&CFWS)?)
    
         (?<word>            (?&atom) | (?&quoted_string))
         (?<phrase>          (?&word)+)
    
         # Folding white space
         (?<FWS>             (?: (?&WSP)* (?&CRLF))? (?&WSP)+)
         (?<ctext>           (?&NO_WS_CTL) | [x21-x27x2a-x5bx5d-x7e])
         (?<ccontent>        (?&ctext) | (?&quoted_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类不可覆盖。
  • 没有调试或分析工具。
  • 缺乏与UTS#18:基本正则表达式支持的兼容性,这是使Java正则表达式对Unicode有用的最基本步骤。 他们目前不是。 他们甚至不支持十年前的Unicode 3.1属性,这意味着您不能以任何合理的方式使用Java模式进行Unicode编码; 基本的积木不存在。
  • 其中,前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?