用于搜索和替换lua中的模式的字符串模式或字符串操作

时间:2015-03-23 11:53:09

标签: string lua lua-patterns

我获取系统上的域列表,我只需要替换包含" 域\用户名"与' *'。

截至目前,我可以使用*使用string.gsub()来掩盖域名,但是我应该添加什么模式以确保将任何域\用户名替换为*

示例: 如果系统中有2个域 test.com work-user.com ,则用户为 admin 来宾文件包含以下详细信息:

用户尝试从TEST \ admin登录;但是应该从work-user \ user1登录,没有用于测试\ guest,account的日志。 域test.com和WORK-USER.org处于活动状态,TESTING域处于非活动状态。

然后输出应如下所示:

用户尝试从*********登录;但是应该从******** \ user1登录,没有用于测试\ *****,帐户的日志。 域****。com和*********.org是活动的,TESTING域处于非活动状态。

由于Testing和user1不是该系统上的域和用户名,因此不应替换它们。

我有逻辑以任何给定的格式独立替换用户名和域名,但是当它是域\用户名的格式时,我无法替换它。

我得到域名后必须添加一些逻辑\模式,以便符合上述要求。 能告诉我怎么办吗?

我尝试了以下代码:

test_string="User tried to login from TEST\\admin; but should have logged in from work-user\\user1, No logs present for testing\\guest, account. The domain test.com and WORK-USER.org are active and TESTING domain in inactive" s= "test" t=( string.gsub(s.."$DNname", "%$(%w+)", {DNname="\\([%w_]+)"}) ) n=( string.gsub(s.."$DNname", "%$(%w+)", {DNname="\\([%a%d]+)([%;%,%.%s]?)"}) ) print (t) print(n) r=string.match(test_string,t) res=string.match(test_string,n) print(r) print(res)

打印nil,无法匹配任何模式

1 个答案:

答案 0 :(得分:2)

首先让我们谈谈您的代码无法正常运作的原因。

首先,你的模式都有反斜杠,所以你没有反斜杠就错过任何东西:

print(t) -- test\([%w_]+)
print(n) -- test\([%a%d]+)([%;%,%.%s]?)

但还有另一个问题。在您的测试消息中唯一应该与反斜杠匹配的是TEST \ admin。但是这里的TEST都是大写的,模式匹配区分大小写,所以你找不到它。

然后,答案的第一部分是制作一个不区分大小写的模式。这可以按如下方式完成:

s= "[Tt][Ee][Ss][Tt]"

在这里,我将每个字母替换为与大写或小写字母匹配的字符类。

如果我们在原始邮件中查找此模式会发生什么?我们将遇到一个不幸的问题:我们将找到测试和测试。看起来你可能已经遇到了这个问题,因为你写了#34;([%;%,%。%s]?)"。

更好的方法是前沿模式。 (请注意,边界模式是Lua 5.1中未记录的功能。我不确定它是否在Lua 5.0中。它在Lua 5.2中成为一个记录的功能。)

前沿模式采用字符集,只匹配字符之间的空格,前一个字符不在集合中,下一个字符在集合中。这听起来很复杂,但基本上它可以让你找到单词的开头或结尾。

要使用前沿模式,我们需要弄清楚域或用户名的外观。我们可能无法完美地做到这一点,但是,在实践中,过度贪婪应该没问题。

s = "%f[%w-][Tt][Ee][Ss][Tt]%f[^%w-]"

这种新模式将匹配" TEST"和"测试",但不匹配" TESTING"或"测试"。

在继续操作之前,让我们来看看您的" work-user"等域名可能出现的问题。角色" - "在模式中有特殊意义,所以我们必须逃避它。可以通过添加"%"来转义所有特殊字符。在前。因此,我们的工作用户模式如下所示:

s = "%f[%w-][Ww][Oo][Rr][Kk]%-[Uu][Ss][Ee][Rr]%f[^%w-]"

嗯,这些模式很难写出来,所以让我们尝试编写一个函数来为我们做这些:

function string_to_pattern(str, frontier_set, ci)
  -- escape magic characters
  str = str:gsub("[][^$()%%.*+-?]", "%%%0")

  if ci then
    -- make the resulting pattern case-insensitive
    str = str:gsub("%a", function(letter)
      return "["..letter:upper()..letter:lower().."]"
    end)
  end

  if frontier_set then
    str = "%f["..frontier_set.."]"..str.."%f[^"..frontier_set.."]"
  end
  return str
end

print(string_to_pattern("work-user", "%w-", true))
  -- %f[%w-][Ww][Oo][Rr][Kk]%-[Uu][Ss][Ee][Rr]%f[^%w-]

我现在就提到一个角落案例:这种模式将不匹配" -work-user"或"工作用户 - "。这可能是好的,取决于生成什么类型​​的消息。你可以采取" - "超出边界集,但是你会匹配,例如"我的工作,用户&#34 ;.您可以决定这是否重要,但我还没有想过如何使用Lua的模式匹配语言来解决它。

现在,我们如何用*'替换匹配?这部分很简单。内置的string.gsub函数将允许我们用其他字符串替换模式的匹配。我们只需要生成一个由字符组成的替换字符串。

function string_to_stars(str)
  return ("*"):rep(str:len())
end

local pattern = string_to_pattern("test", "%w-", true)
print( (test_string:gsub(pattern, string_to_stars)) )

现在,这是最后一个问题。我们可以匹配我们匹配域中的用户。例如:

-- note that different frontier_set here
-- I don't know what the parameters for your usernames are,
-- but this matches your code
local pattern = string_to_pattern("admin", "%w_", true)
print( (test_string:gsub(pattern, string_to_stars)) )

但是,即使我们单独替换所有域和用户名,也可以使用#34; TEST"之间的反斜杠。和" admin"在" TEST \ admin"不会被取代。我们可以做这样的黑客攻击:

test_string:gsub("%*\\%*","***")

这将取代" **"用" ***"在最终输出中。然而,这不是很强大,因为它可以取代" **"这是在原始邮件中,而不是我们处理的结果。要正确地执行操作,我们必须遍历所有域+用户对并执行以下操作:

test_string:gsub(domain_pattern .. "\\" .. user_pattern, string_to_stars)

请注意,这必须在任何其他替换之前完成,否则域名和用户名将被替换,并且无法再匹配。

现在问题以这种方式解决了,让我建议一种替代方法,它反映出我将从头开始编写的内容。我认为它可能更简单,更易读。我们不是使用模式匹配来准确找到我们的域和用户名,而是只匹配可能是域或用户名的令牌,然后检查它们是否完全匹配。

local message = -- broken into multiple lines only for
                -- formatting reasons
  "User tried to login from TEST\\admin; but should "
  .."have logged in from work-user\\user1, No logs present "
  .."for testing\\guest, account. The domain test.com and "
  .."WORK-USER.org are active and TESTING domain in inactive"

-- too greedy, but may not matter in your case
local domain_pattern = "%w[%w-]*"
-- again, not sure
local user_pattern = "[%w_]+"

-- for case-insensitivity, call :lower before inserting into the set
local domains = {["test"]=true, ["work-user"]=true}
local users = {["admin"]=true, ["guest"]=true}

local pattern = "(("..domain_pattern..")\\("..user_pattern.."))"
message = message:gsub(pattern, function(whole, domain, user)
  -- only call lower if case-insensitive
  if domains[domain:lower()] and users[user:lower()] then
    return string_to_stars(whole)
  else
    return whole
  end
end)

local function replace_set(message, pattern, set, ci)
  return (message:gsub(pattern, function(str)
    if ci then str = str:lower() end
    if set[str] then
      return string_to_stars(str)
    else
      return str
    end
  end))
end

message = replace_set(message, domain_pattern, domains, true)
message = replace_set(message, user_pattern, users, true)

print(message)

请注意此示例中的模式有多简单。我们不再需要不区分大小写的字符类,如" [Tt]"因为在匹配之后检查不区分大小写是通过强制使用string.lower将这两个字符串设置为小写(这可能不是最大效率,但是,嘿,这是Lua)。我们不再需要使用前沿模式,因为我们保证因为贪婪匹配而得到完整的单词。反斜杠的情况仍然很奇怪,但我已经在相同的"强大的"我上面建议的方式。

最后一点:我不知道你为什么要这样做,但我可以猜测是为了防止有人看到域名或用户名。用*替换它们不一定是最好的方法。首先,如果您的消息(例如)用字母分隔,那么以这些方式进行匹配可能会有问题。对于用户友好的消息来说,这似乎不太可能,但我不知道当安全性受到威胁时,您是否应该依赖这些内容。另一件事是你没有隐藏域或用户名的长度。这也可能是不安全的主要根源。例如,用户可能会合理地猜测*****是" admin"。