我需要一个非常简单的字符串验证器,它会显示第一个符号与所需格式不对应的位置。我想使用正则表达式,但在这种情况下,我必须找到字符串停止对应表达式的地方,我无法找到一个方法来做到这一点。 (它必须是一个相当简单的方法......也许没有一个?)
例如,如果我有正则表达式:
/^Q+E+R+$/
字符串:
"QQQQEEE2ER"
期望的结果应为7
答案 0 :(得分:5)
一个想法:你可以做的是标记你的模式并用可选的嵌套捕获组写它:
^(Q+(E+(R+($)?)?)?)?
然后,您只需要计算获得的捕获组的数量,以了解正则表达式引擎在模式中停止的位置,并且可以确定字符串中匹配结尾与整个匹配长度的偏移量。
正如@ zx81在他的评论中注意到的那样,如果其中一个元素可以匹配下一个元素(例子Q可以匹配元素E),那么事情会变得不同。
假设Q是\w
(并且可以匹配E和R)。对于字符串QQQEEERRR
,当\w+
将提供三个组时,先前模式将只提供一个捕获组(贪婪的^(\w+)(E+)(R+)$
匹配所有):QQQEE
,E
,RRR
要获得相同的结果,您需要添加更改:
^((?:\w+(?=E)|\w+)(E+(R+($)?)?)?)?
在交替中,必须首先测试E存在的情况,并且只有当该分支失败时(使用超前),才使用E不存在的另一个分支。
因此,可以像这样重写完整模式来处理这种特定情况:
^((?:Q+(?=E)|Q+)((?:E+(?=R)|E+)((?:R+(?=$)|R+)($)?)?)?)?
也许你可以看一下gem amatch。
答案 1 :(得分:2)
这是一个有趣的任务,可以用一个简洁的正则表达式技巧来完成:
^(?:(?=(Q+)))?(?:(?=(Q+E+)))?(?:(?=(Q+E+R+)))?(?:(?=(Q+E+R+$)))?
我们有四个可选的前瞻,检查模式的各个部分,并逐步捕获到第1,2,3和4组的部分匹配。
Q+
,例如QQQQ
。Q+E+
(如果可以匹配),在您的示例EEE
中。Q+E+R+
,例如nil
。Q+E+R+$
,例如nil
。在您的代码中,检查通过测试!$1.nil?
,!$2.nil?
等设置的最后一个组。
最后一组为您提供了可匹配的长度,因此在您的示例中,$2.length
会为您提供所需的7
。
顺便说一下,第2组是最后一组的事实也告诉你我们在R+
失败了。
答案 2 :(得分:2)
对于您的示例,您可以执行以下操作。
<强>代码强>
从以下位置更改正则表达式:
/^Q+E+R+$/
到
R = /^(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_matched_chars(str) == str.size
才匹配原始正则表达式
<强>实施例强>
nbr_matched_chars("QQQQEEE2ER") #=> 7
nbr_matched_chars("QQQQEEEERR") #=> 10 (= "QQQQEEEERR".size)
nbr_matched_chars("QQAQQEEEER") #=> 2
<强>解释强>
要了解为什么[显然:-)]有效,我们可以查看调用String#scan的结果,然后是Array#flatten:
"QQQQEEE2ER".scan(r).flatten #=> ["QQQQ", "EEE" , nil ]
"QQQQEEEERR".scan(r).flatten #=> ["QQQQ", "EEEE", "RR"]
"QQAQQEEEER".scan(r).flatten #=> ["QQ" , nil , nil ]