LPeg模式匹配不包含连续的横线的字符串

我正在尝试编写一个LPeg模式来匹配以下字符串:

  • 以字母开头
  • 其后包含字母数字字符
  • 不包含两个或两个以上的连续横线(例如,不允许 test--string

参考正则表达式[a-zA-Z](-?[a-zA-Z0-9])*匹配我想要的内容。

以下是我正在使用的代码,仅供参考:

require "lpeg"
P,R,C = lpeg.P,lpeg.R,lpeg.C

dash  = P"-"
ucase  = R"AZ"
lcase  = R"az"
digit  = R"09"
letter = ucase + lcase
alphanum = letter + digit

str_match = C(letter * ((dash^-1) * alphanum)^0)

strs = {
    "1too",
    "too0",
    "t-t-t",
    "t-t--t",
    "t--t-t",
    "t-1-t",
    "t--t",
    "t-one1",
    "1-1",
    "t-1",
    "t",
    "tt",
    "t1",
    "1",
}

for _,v in ipairs(strs) do
    if lpeg.match(str_match,v) ~= nil then
        print(v," => match!")
    else
        print(v," => no match")
    end
end

但是,令我非常沮丧的是,我得到了以下输出:

1too     => no match
too0     => match!
t-t-t    => match!
t-t--t   => match!
t--t-t   => match!
t-1-t    => match!
t--t     => match!
t-one1   => match!
1-1      => no match
t-1      => match!
t        => match!
tt       => match!
t1       => match!
1        => no match

不管代码输出是什么,t-t--tt--t-tt--t都不应匹配。

点赞
用户234175
用户234175

在你的模式 letter * ((dash^-1) * alphanum)^0 中,lpeg 会尝试匹配字符串的前缀。对于没有预期匹配的情况

t-t--t

t--t-t

t--t

加粗部分是你的模式成功匹配的位置。如果没有捕获,lpeg.match 返回它能够使用你的模式解析的最后一个位置(一个数字)。对于这三种情况,都被匹配子部分捕获了,这就解释了你看到的错误输出。

如果您只是一个一个地匹配每个字符串,您可以修改您的模式以检查在解析之后是否还有剩余字符。

str_match = C(letter * ((dash^-1) * alphanum)^0) * -1

同样可以使用 lpeg.re 模块

re_pat = re.compile "{ %a ('-'? %w)* } !."

对于流匹配或在目标字符串中找到所有模式的出现,堆栈语法规则如下

stream_parse = re.compile
[[
  stream_match  <- ((str_match / skip_nonmatch) delim)* str_match?
  str_match     <- { %a ('-'? %w)* } (&delim / !.)
  skip_nonmatch <- !str_match (!delim .)*

  delim         <- %s+
]]

任何匹配都将被捕获并返回。如果没有匹配,你将得到 nil 或一个数字,表示模式停止解析的字符串位置。

编辑: 对于需要在无匹配时返回 nil 的情况,这个语法的调整应该可以解决问题

stream_parse = re.compile
[[
  stream_match  <- (str_match / skip_nonmatch+ &str_match)+
  str_match     <- { %a ('-'? %w)* } (&delim / !.)
  skip_nonmatch <- !str_match (!delim .)* delim

  delim         <- %s+
]]
2016-12-29 01:14:06