首先让我们谈谈为什么您的代码不起作用。
一方面,你的模式都有一个反斜杠,所以你马上就会错过任何没有反斜杠的东西:
print(t) -- test\([%w_]+)
print(n) -- test\([%a%d]+)([%;%,%.%s]?)
但是还有另一个问题。唯一应该在您的测试消息中匹配的反斜杠是 TEST\admin。但是这里的TEST都是大写的,而且模式匹配是区分大小写的,所以你不会找到的。
因此,答案的第一部分是创建一个不区分大小写的模式。这可以按如下方式完成:
s= "[Tt][Ee][Ss][Tt]"
在这里,我将每个字母替换为匹配大写或小写字母的字符类。
如果我们在原始消息中寻找这种模式会发生什么?我们将遇到一个不幸的问题:我们会发现测试和测试。看来您在编写“([%;%,%.%s]?)”时可能已经遇到过这个问题。
更好的方法是边界模式。 (请注意,边界模式在 Lua 5.1 中是一个未记录的功能。我不确定它是否在 Lua 5.0 中。它在 Lua 5.2 中成为了一个记录功能。)
边界模式接受一个字符集,并且只匹配字符之间的空格,其中前一个字符不在该集中,而下一个字符在该集中。这听起来很复杂,但基本上它可以让您找到单词的开头或结尾。
要使用边界模式,我们需要弄清楚域或用户名可能是什么样子。我们可能无法完美地做到这一点,但在实践中,过度贪婪应该没问题。
s = "%f[%w-][Tt][Ee][Ss][Tt]%f[^%w-]"
这个新模式将匹配“TEST”和“test”,但不会匹配“TESTING”或“testing”。
在继续之前,让我们先看看像您的“工作用户”这样的域可能会出现的问题。字符“-”在模式中具有特殊的含义,所以我们必须对其进行转义。所有特殊字符都可以通过在前面添加“%”来转义。因此,我们的工作-用户模式如下所示:
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”或“work-user-”。根据生成的消息类型,这可能可以或不可以。您可以将“-”从边界集中取出,但随后您将匹配例如“我的工作用户”。你可以决定这是否重要,但我还没想过如何使用 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)) )
但是,即使我们单独替换所有域和用户名,“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 强制两个字符串都为小写来检查不区分大小写(这可能不是最有效的,但是,嘿,这是卢阿)。我们不再需要使用边界模式,因为贪婪匹配可以保证我们得到完整的单词。反斜杠的情况仍然很奇怪,但我已经按照我上面建议的“稳健”方式处理了它。
最后一点:我不知道您这样做的确切原因,但我可能猜到这是为了防止某人看到域或用户名。用 * 替换它们不一定是最好的方法。首先,如果您的消息(例如)用字母分隔,则以这些方式进行匹配可能会出现问题。对于用户友好的消息来说,这似乎不太可能,但我不知道当安全受到威胁时,你是否应该指望这件事。另一件事是您没有隐藏域或用户名的长度。这也可能是不安全的主要来源。例如,用户可能合理地猜测 ***** 是“admin”。