Строковый шаблон или манипуляции со строками для поиска и замены шаблона в lua

Я получаю список доменов в системе, и мне нужно заменить только те шаблоны, которые содержат "домен\имя пользователя" на "*".

На данный момент я могу маскировать доменные имена с помощью *, используя string.gsub(), но какой шаблон я должен добавить, чтобы убедиться, что любое присутствие домена\имя пользователя заменено на *

Пример: если в системе есть 2 домена test.com и work-user.com и пользователи с правами admin > и guest файл содержит следующие данные:

Пользователь пытался войти из TEST\admin; но должен был войти в систему с рабочего пользователя\пользователя1, нет журналов для тестирования\гость, учетная запись. Домен 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)

Он печатает ноль и не может сопоставить какой-либо шаблон


person Namitha    schedule 23.03.2015    source источник
comment
Обновленный код: 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) Печатает ноль и не может сопоставить какой-либо шаблон   -  person Namitha    schedule 23.03.2015
comment
@Yu Hao: добавлен код к вопросу. Не могли бы вы рассказать мне, как сопоставить строку + шаблон?   -  person Namitha    schedule 23.03.2015


Ответы (1)


Сначала поговорим о том, почему ваш код не работает.

Во-первых, в ваших шаблонах есть обратная косая черта, поэтому без обратной косой черты вы ничего не пропустите:

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».

person tehtmi    schedule 24.03.2015
comment
@tehmi: Очень хорошо объяснил. Благодарю вас! - person Namitha; 24.03.2015