在Lua 5.1中将可重复字符串匹配为“整个单词”

时间:2019-02-04 04:08:51

标签: regex lua lua-5.1 lpeg

我的环境:

  • Lua 5.1
  • 绝对不能使用带有本地组件(例如C .so / .dll)的库
  • 我可以运行任意的纯Lua 5.1代码,但无法访问os和其他几个允许访问本机文件系统,shell命令或类似内容的软件包,因此必须实现所有功能在Lua本身中(仅限)。
  • 我已经设法加入LuLpeg。我可能可以引入其他纯Lua库。

我需要编写一个函数,如果输入字符串匹配任意字母和数字序列作为一个整个单词,则该函数返回true,该单词重复一次或多次,并且可能带有标点符号在整个匹配子字符串的开头或结尾。我使用的是“整个单词”,其含义与PCRE单词边界\b相同。

为了证明这个想法,这是使用LuLpeg的re模块的不正确尝试;似乎可以使用否定的前瞻功能,但不能消除否定的外观后面

function containsRepeatingWholeWord(input, word)
    return re.match(input:gsub('[%a%p]+', ' %1 '), '%s*[^%s]^0{"' .. word .. '"}+[^%s]^0%s*') ~= nil
end

以下是示例字符串和预期的返回值(引号是句法的,就像键入Lua解释器一样,而不是字符串的文字部分;这样做是为了使尾部/前导空格显而易见):

  • 输入: " one !tvtvtv! two"单词: tv返回值: true
  • 输入: "I'd"单词: d返回值: false
  • 输入: "tv"单词: tv返回值: true
  • 输入: " tvtv! "单词: tv返回值: true
  • 输入: " epon "单词: nope返回值: false
  • 输入: " eponnope "单词: nope返回值: false
  • 输入: "atv"单词: tv返回值: false

如果我拥有完整的PCRE正则表达式库,则可以快速完成此操作,但我不能这样做,因为我无法链接到C,而且我还没有发现PCRE或类似的任何纯粹Lua实现。

我不确定LPEG是否足够灵活(直接使用LPEG或通过其re模块使用)来完成我想做的事情,但是我很确定内置的Lua函数不能做我想做的事情我想要,因为它无法处理重复的字符序列。 (tv)+不适用于Lua内置的string:match功能和类似功能。

我一直在寻找有趣的资源,试图弄清楚该怎么做,但无济于事:

2 个答案:

答案 0 :(得分:3)

我认为该模式不能可靠地工作,因为%s*[^%s]^0部分匹配了一系列可选的空格字符,后跟非空格字符,然后它尝试匹配重复的单词,但失败了。之后,它不会在字符串中向后或向前移动,而是尝试在另一个位置匹配重复的单词。 LPeg和re的语义与大多数正则表达式引擎的语义非常不同,即使对于看起来相似的事物也是如此。

这是基于re的版本。该模式具有单个捕获(重复的单​​词),因此,如果找到重复的单词,则匹配返回字符串而不是数字。

function f(str, word)
    local patt = re.compile([[
        match_global <- repeated / ( [%s%p] repeated / . )+
        repeated <- { %word+ } (&[%s%p] / !.) ]],
        { word = word })
    return type(patt:match(str)) == 'string'
end

这有点复杂,因为香草re无法生成lpeg.B模式。

以下是使用lpeg的{​​{1}}版本。 LuLPeg也可以在这里工作。

lpeg.B

答案 1 :(得分:2)

Lua模式足够强大。
此处不需要LPEG。

这是你的功能

function f(input, word)
   return (" "..input:gsub(word:gsub("%%", "%%%%"), "\0").." "):find"%s%p*%z+%p*%s" ~= nil
end

这是对该功能的测试

for _, t in ipairs{
   {input = " one !tvtvtv! two", word = "tv", return_value = true},
   {input = "I'd", word = "d", return_value = false},
   {input = "tv", word = "tv", return_value = true},
   {input = "   tvtv!  ", word = "tv", return_value = true},
   {input = " epon ", word = "nope", return_value = false},
   {input = " eponnope ", word = "nope", return_value = false},
   {input = "atv", word = "tv", return_value = false},
} do
   assert(f(t.input, t.word) == t.return_value)
end