将 POSIX 正则表达式移植到 Lua 模式 - 意外结果

我正在尝试将 POSIX regex 移植到 Lua 字符串模式中,但遇到了困难。

我正在处理 HTML 响应,我想要过滤已选中的复选框。我特别关注每个已选中的复选框的“value”和“name”字段:

以下是我感兴趣的复选框示例:

<input class="rid-2 form-checkbox" id="edit-2-access-comments" name="2[access comments]" value="access comments" checked="checked" type="checkbox">

<input class="rid-3 form-checkbox real-checkbox" id="edit-3-administer-comments" name="3[administer comments]" value="administer comments" checked="checked" type="checkbox">

相反,我不感兴趣的是这种(未选中的)复选框:

<input class="rid-2 form-checkbox" id="edit-2-access-printer-friendly-version" name="2[access printer-friendly version]" value="access printer-friendly version" type="checkbox">

使用 POSIX regex,在 Python 中我使用了以下模式:pattern=r'name="(.*)" value="(.*)" checked="checked"',这个方法很有效。

我的第一个尝试是在 Lua 中简单使用以下模式:pattern ='name="(.-)" value="(.-)" checked="checked"',但这导致奇怪的结果(第一个捕获了预期的内容,但第二个捕获了大量不需要的 HTML)。

我还尝试了以下模式:

pattern = 'name="(%d?%[.-%])" value="(.-)"%s?(c?).-="?c.-"%s?type="checkbox"'

这一次,“value”的内容在第二个捕获中返回,但所有复选框都匹配了(而不仅仅是那些具有“checked =“checked””字段的复选框)。

为了完整起见,这里是我的 Lua 代码(来自我的 Nmap NSE 脚本),试图进行这种模式匹配:

  pattern = 'name="(.-)" value="(.-)" checked="checked"'
  data = {}
  for name, value in string.gmatch(res.body, pattern) do
    stdnse.debug(1, string.format("%s %s", name, value))
  end
点赞
用户1442917
用户1442917

更新基于评论

当输入中存在一个不带 checked="checked" 的行在一个带该属性的行之前时,.- 表达式会捕获不必要的部分,因而无法匹配成功。有几种避免这种情况的方法。@EgorSkriptunoff 提出了一种方法,即使用 ([^"]*) 作为匹配模式;另一种方法是排除新的行 ([^\r\n]-)。以下示例将会按你的预期输出结果:

local s = [[
<input class="rid-2 form-checkbox" id="edit-2-access-comments" name="2[access comments]" value="access comments" checked="checked" type="checkbox">
<input class="rid-2 form-checkbox" id="edit-2-access-printer-friendly-version" name="2[access printer-friendly version]" value="access printer-friendly version" type="checkbox">
<input class="rid-3 form-checkbox real-checkbox" id="edit-3-administer-comments" name="3[administer comments]" value="administer comments" checked="checked" type="checkbox">
]]
local pattern = 'name="([^\r\n]-)" value="([^\r\n]-)" checked="checked"'
for name, value in string.gmatch(s, pattern) do
  print(name, value)
end

输出结果:

2[access comments]  access comments
3[administer comments]  administer comments
2015-10-01 17:32:16
用户3832970
用户3832970

我已经在Python中使用了以下模式:pattern=r'name="(.*)" value="(.*)" checked="checked"',它可以正常工作。

Python的re不符合POSIX规范,.匹配除换行符以外的任何字符(在POSIX和Lua中,.匹配包括换行符在内的任何字符)。

如果您想匹配依次具有上述3个属性的字符串,则应使用类似以下内容的内容:

local pattern = 'name="([^"]*)"%s+value="([^"]*)"%s+checked="checked"'

为什么不使用[^\r\n]-?因为如果一行中有两个标记,第一个标记具有第一个和/或第二个属性,第二个标记具有第二个和第三个属性或仅具有第二个属性(即使第三个标记具有第三个属性,而第一个标记包含前两个属性),则将有匹配,因为[^\r\n]匹配<>,并且可以跨越标记“过火”。

注意,否定的括号表达式[^"]*只会匹配除 "以外的 0 或 多个字符,从而限制匹配在一个标记中。

请参阅Lua demo

local rx = 'name="([^"]*)"%s+value="([^"]*)"%s+checked="checked"'
local s = '<li name="n1"\nvalue="v1"><li name="n2"\nvalue="v1" checked="checked"><li name="n3"\nvalue="v3"   checked="checked">'
for name, value in string.gmatch(s, rx) do
  print(name, value)
end

输出:

n2  v1
n3  v3
2018-05-04 20:00:48