Ruby 正则表达式在哪个位置失败?

Ruby 正则表达式在哪个位置失败?,ruby,regex,string,syntax,substring,Ruby,Regex,String,Syntax,Substring,我需要一个非常简单的字符串验证器,它将显示第一个符号与所需格式不对应的位置。我想使用正则表达式,但在这种情况下,我必须找到字符串与表达式相对应的停止位置,而我找不到一个可以这样做的方法。 (这一定是一个相当简单的方法……也许没有?) 例如,如果我有正则表达式: /^Q+E+R+$/ 带字符串: "QQQQEEE2ER" 期望的结果应该是7这是一个有趣的任务,可以通过一个整洁的正则表达式技巧来完成: ^(?:(?=(Q+)))?(?:(?=(Q+E+)))?(?:(?=(Q+E+R+)))?(

我需要一个非常简单的字符串验证器,它将显示第一个符号与所需格式不对应的位置。我想使用正则表达式,但在这种情况下,我必须找到字符串与表达式相对应的停止位置,而我找不到一个可以这样做的方法。 (这一定是一个相当简单的方法……也许没有?)

例如,如果我有正则表达式:

/^Q+E+R+$/
带字符串:

"QQQQEEE2ER"

期望的结果应该是7

这是一个有趣的任务,可以通过一个整洁的正则表达式技巧来完成:

^(?:(?=(Q+)))?(?:(?=(Q+E+)))?(?:(?=(Q+E+R+)))?(?:(?=(Q+E+R+$)))?
我们有四个可选的lookahead检查模式的各个部分,并以增量方式捕获组1、2、3和4的部分匹配

  • 组1包含
    Q+
    ,如果可以匹配,在您的示例中为
    qqq
  • 组2包含
    Q+E+
    ,如果可以匹配,在您的示例中为
    EEE
  • 组3包含
    Q+E+R+
    ,如果可以匹配,在您的示例中为
    nil
  • 组3包含
    Q+E+R+$
    如果可以匹配,在您的示例中为
    nil
  • 在代码中,检查通过测试
    设置的最后一个组$1.无?
    $2.无?

    最后一组为您提供了可匹配的长度,因此在您的示例中,
    $2.length
    为您提供了所需的
    7


    顺便说一句,组2是最后一组的事实也告诉您,我们在
    R+

    上失败了一个想法:您可以做的是标记您的模式,并使用可选的嵌套捕获组编写它:

    ^(Q+(E+(R+($)?)?)?)?
    
    然后,您只需要计算获得的捕获组的数量,就可以知道正则表达式引擎在模式中的停止位置,并且您可以使用整个匹配长度确定字符串中匹配结束的偏移量

    正如@zx81在他的评论中所注意到的,如果其中一个元素可以匹配下一个元素(例如Q可以匹配元素E),那么情况就不同了

    假设Q是
    \w
    (并且可以匹配E和R)。对于字符串
    qqeerr
    ^(\w+(E+)(R+)$
    将给出三个组时,先前的模式将只给出一个捕获组(贪婪的
    \w+
    匹配所有组):
    qqee
    E
    RRR

    要获得相同的结果,您需要添加一个备选方案:

    ^((?:\w+(?=E)|\w+)(E+(R+($)?)?)?)?
    
    在替代方案中,必须首先测试E存在的情况,并且只有当该分支失败(使用前瞻)时,才使用E不存在的另一个分支

    因此,可以像这样重写完整模式,以处理此特定情况:

    ^((?:Q+(?=E)|Q+)((?:E+(?=R)|E+)((?:R+(?=$)|R+)($)?)?)?)?
    

    也许你也可以看看gem。

    举个例子,你可以做以下几点

    代码

    将您的正则表达式更改为:

        /^Q+E+R+$/
    

    然后对字符串应用以下方法:

    def nbr_matched_chars(str)
       str.scan(R).flatten.reduce(0) {|t,e| return t if e.nil?; t+e.size } 
    end
    
    str
    匹配原始正则表达式当且仅当
    nbr\u matched\u chars(str)=str.size

    示例

    nbr_matched_chars("QQQQEEE2ER") #=>  7
    nbr_matched_chars("QQQQEEEERR") #=> 10 (= "QQQQEEEERR".size)
    nbr_matched_chars("QQAQQEEEER") #=>  2
    
    解释

    要了解这[显然:-)]起作用的原因,我们可以查看调用的结果,然后是:


    就我个人而言,我希望有办法创建一个Regexp对象,然后让它“尝试并匹配”一个字符串,然后让它在第一次找不到匹配项时录制。这听起来像是一种“通用”的方式,因为正则表达式引擎可能知道它什么时候失败,尽管我正在阅读,而且看起来即使这样也可能不是一种可靠的优化方法<代码>/^(Q+(E+(R+($)?)?)?/。匹配('QQEE2ER')[0]。长度给出OP正在查找的
    7
    。如果它返回的值等于测试字符串的长度,则匹配成功。谢谢,但是手工构建模式可能有点苛刻,几乎不可能从原始模式自动构建模式。@JackWatson:我理解你的问题,但在这种情况下,您只需检查是否存在必须与@匹配的捕获组。稍后可以提取长度。请记住,此模式始终匹配任何字符串。@CasimimiritHippolyte再看一次,现在我觉得您的解决方案在测试
    Q+E+R+
    中的标记时非常好,但如果标记不是互斥的,例如
    ^(\w+(E+(R+($)?)?)?
    ,则会出现故障,正如你所知,这是一种常见的情况。在
    QER
    Q
    QE
    等上,这表明只设置了一个组,而实际上
    ^\w+E+R+
    的所有令牌都参与了匹配。在这种情况下,我的解决方案仍然有效,因此在我看来,它可能更一般。虽然我会记住你的相互排斥的情况。你觉得怎么样?@Casimirithippolyte有点像黑客(我相信你会同意的),但就是这样,干得好。:)我花了很长时间才看完你的答案(+1),但我还没看完。你看,我快速浏览了你的个人资料,决定看看你在SO最糟糕(哈!)和最好的回答经历,现在我已经完成了一部分…@CarySwoveland哈,我也喜欢阅读别人的帖子。:)
    nbr_matched_chars("QQQQEEE2ER") #=>  7
    nbr_matched_chars("QQQQEEEERR") #=> 10 (= "QQQQEEEERR".size)
    nbr_matched_chars("QQAQQEEEER") #=>  2
    
    "QQQQEEE2ER".scan(r).flatten #=> ["QQQQ", "EEE" , nil ]
    "QQQQEEEERR".scan(r).flatten #=> ["QQQQ", "EEEE", "RR"]
    "QQAQQEEEER".scan(r).flatten #=> ["QQ"  , nil   , nil ]