正则表达式的继承者?

mik*_*kel 14 regex

看一下SO上常见的一些正则表达式问题,在我看来,传统的正则表达式语法在很多领域都缺乏人们现在正在寻找它的任务.例如:

  • 我想匹配1到31之间的数字,我该怎么做?

通常的答案是不要使用正则表达式,使用正常的条件比较.如果你自己只得到这个数字就好了,但是当你想把这个数字作为一个更长的字符串的一部分来匹配时,那就不那么好了.为什么我们不能编写类似的东西\d{1~31},要么修改正则表达式来进行某种形式的计数,还是让正则表达式引擎在内部将它转换成[1-9]|[12]\d|3[01]

  • 如何匹配特定字符串的偶数/奇数次出现?

这导致了一个非常混乱的正则表达式,能够做到这一点会很棒(mytext){Odd}.

  • 如何使用正则表达式解析XML?

我们都知道这是个坏主意,但如果[^ ]操作员不仅限于一个角色,那么这个和类似的任务会更容易.能够这样做真好<name>(.*)[^(</name>)]

  • 如何使用正则表达式验证电子邮件?

非常常见,但正确使用正则表达式非常复杂.如果{IsEmail}可以使用类似的语法,那么每个人都必须重新发明轮子.


我确信还有其他一些也很有用.我不太了解正则表达式的内部结构,知道这些实现有多么容易,或者甚至是否可能.实现某种形式的计数(解决前两个问题)可能意味着它不再是技术上的"正则表达式",但它肯定会有用.

"正则表达式2.0"语法是否可取,技术上可行,是否有人在处理这样的事情?

Gab*_*abe 16

我相信Larry Wall用Perl 6正则表达式覆盖了它.基本思想是用更有用的语法规则替换简单的正则表达式.它们更容易阅读,并且更容易将代码放入以确保您拥有多个匹配项.另外,你可以命名规则IsEmail.我不可能在这里列出所有细节,但足以说,它听起来像你的建议.

以下是http://dev.perl.org/perl6/doc/design/exe/E05.html中的一些示例:

匹配的IP地址:

token quad {  (\d**1..3) <?{ $1 < 256 }>  }
$str ~~ m/ <quad> <dot> <quad> <dot> <quad> <dot> <quad> /;
Run Code Online (Sandbox Code Playgroud)

匹配嵌套括号:

$str =~ m/ \(  [ <-[()]> + : | <self> ]*  \) /;
Run Code Online (Sandbox Code Playgroud)

注释:

    $str =~ m/ <'('>                # Match a literal '('
               [                    # Start a non-capturing group
                    <-[()]> +       #    Match a non-paren (repeatedly)
                    :               #    ...and never backtrack that match
               |                    # Or
                    <self>          #    Recursively match entire pattern
               ]*                   # Close group and match repeatedly
               <')'>                # Match a literal ')'
             /;
Run Code Online (Sandbox Code Playgroud)

  • @Andrew:你读过这个问题了吗?他并没有试图找到编写每个可能的解析器的工具.他只是想扩展一个非常受欢迎的工具来完成它可以*差不多做的任务.例如,标准钻头作为螺丝刀是无用的,因为它只是向前和高速前进.一旦你使它可逆和变速,它是一个伟大的螺丝刀(和螺丝刀等).OP只是希望为一个很好的工具添加一些功能以使其更好 - 或者只是创建一个可以做同样事情的新工具. (2认同)

And*_*ore 16

不要怪工具,责怪用户.

构建正则表达式以匹配字符串中的模式.就是这样.

它不是为了:

  • 整数验证
  • 标记语言解析
  • 非常复杂的验证(即:RFC 2822)
  • 精确的字符串比较
  • 拼写纠正
  • 矢量计算
  • 遗传解码
  • 奇迹制作
  • 宝贝救人
  • 财务管理
  • 子原子分区
  • 助焊剂电容激活
  • Warp核心引人入胜
  • 时间旅行
  • 头痛诱导
    永远不要介意最后一个.似乎正则表达式非常适合于在他们不应该使用的地方执行最后一项任务.

我们应该重新设计螺丝刀,因为它不能钉?,用锤子.

只需使用适当的工具完成任务.停止使用正则表达式来处理他们不符合条件的任务.

  • 我想匹配1到31之间的数字,我该怎么做?
    使用您的语言结构尝试将字符串转换为整数并进行适当的比较.

  • 如何匹配特定字符串的偶数/奇数次出现?
    正则表达式不是字符串解析器.但是,如果只需要解析原始字符串的子部分,则可以使用正则表达式提取相关部分.

  • 如何使用正则表达式解析XML?
    你没有.根据需要使用XML或HTML解析器.此外,XML解析器无法完成HTML解析器的工作(除非您有完美形成的XHTML文档),反之亦然.

  • 如何使用正则表达式验证电子邮件?
    你要么使用这个大的憎恶,要么用解析器正确地做.

  • 不要采取错误的方式,但这个答案似乎非常情绪化,我认为大部分都是咆哮.我们的部分工作是构建更好的工具:OP正在询问这些工具,并希望讨论它们可能的用处.事实上他是正确的:许多正则表达式的味道确实提供了扩展,使更强大的能力,使它们成为更多任务的合适工具(更不用说代码标注,严格欺骗`:)`). (4认同)
  • @Gabe:最终,验证的逻辑不应该存储在Apache配置或数据库中.如果您的系统具有可变输入,则存储您可以解析的参数,然后您的代码将发送给适当的验证器.例如``int | required | min:13"`用于您的年龄验证,``email | required"`用于您的电子邮件.如果你确实有一些具有特定格式的东西并且可以使用正则表达式轻松验证,那么无论如何:``regex | pattern:/ ^ REF [0-9] {5,8} $/i"`但是除此之外,不要. (3认同)
  • 关键是我们经常发现自己需要做一些事情,比如验证输入("这是一个有效的电子邮件吗?","这个年龄至少是13岁吗?")并且你不能将YACC语法粘贴到在Apache配置中的行或数据库中的`VARCHAR(100)`字段.为这种任务制作更好的工具似乎并不合理. (2认同)
  • 我认为Kobi是对的.问题是我们中的许多人经常被赋予一个工具(其中验证框架和Apache模块仅仅是这种情况的两个例子)并且被迫使用该工具执行我们的所有任务.如果我们可以选择一个更通用的工具来成为我们的工具,我们可以在不雇用专业人员的情况下完成更多常见的工作(例如LEX和YACC,ANTLR等). (2认同)

kri*_*iss 5

可能它已经存在并且很久以前了.它被称为"语法".有没有听说过yacc和lex?现在需要一些简单的东西.可能看起来很奇怪,正则表达式的强大之处在于它们在现场编写非常简单.

我相信一些(但很大的)专业领域已经有了所需要的东西.我正在考虑XPath语法.

是否有更大的(不限于XML,但仍然很简单)替代方案可以覆盖所有情况?也许你应该看看perl 6 grammars.


ike*_*ami 5

所有这些在Perl中都是合理可行的.

要使1..31与正则表达式匹配:

/( [0-9]+ ) (?(?{ $^N < 1 && $^N > 31 })(*FAIL)) /x
Run Code Online (Sandbox Code Playgroud)

生成如下内容[1-9]|[12]\d|3[01]:

use Regexp::Assemble qw( );
my $ra = Regexp::Assemble->new();
$ra->add($_) for (1..31);
my $re = $ra->re;                 # qr/(?:[456789]|3[01]?|1\d?|2\d?)/
Run Code Online (Sandbox Code Playgroud)

Perl 5.10+使用try来优化替换,因此以下内容应该足够:

my $re = join '|', 1..31;
$re = qr/$re/;
Run Code Online (Sandbox Code Playgroud)

要匹配偶数次出现:

/ (?: pat{2} )* /x
Run Code Online (Sandbox Code Playgroud)

要匹配奇数次出现:

/ pat (?: pat{2} )* /x
Run Code Online (Sandbox Code Playgroud)

模式否定匹配:

/<name> (.*?) </name>/x  # Non-greedy matching

/<name> ( (?: (?!</name>). )* ) </name>/x
Run Code Online (Sandbox Code Playgroud)

要获得与电子邮件地址匹配的模式:

use Regexp::Common qw( Email::Address );
/$RE{Email}{Address}/
Run Code Online (Sandbox Code Playgroud)