Жадные/Нежелательные соответствия шаблонов и необязательные суффиксы в Lua - программирование
Подтвердить что ты не робот

Жадные/Нежелательные соответствия шаблонов и необязательные суффиксы в Lua

В Lua, я пытаюсь сопоставить образ и захватить:

+384 Critical Strike (Reforged from Parry Chance)

а

(+384) (Critical Strike)

где суффикс (Reforged from %s) является необязательным.

Длинная версия

Я пытаюсь сопоставить строку в Lua, используя шаблоны (т.е. strfind)

Примечание. В Lua они не называют их регулярными выражениями, они называют их шаблонами, потому что они не regular.

Примеры строк:

+384 Critical Strike
+1128 Hit

Это разбивается на две части, которые я хочу захватить:

enter image description here

  • Число с лидирующим положительным или отрицательным индикатором; int его случай +384
  • Строка в этом случае Critical Strike.

Я могу захватить их, используя довольно простой шаблон:

enter image description here

И этот шаблон в lua работает:

local text = "+384 Critical Strike";
local pattern = "([%+%-]%d+) (.+)";
local _, _, value, stat = strfind(text, pattern);
  • value = +384
  • stat = Critical Strike

Трудная часть

Теперь Мне нужно расширить этот шаблон регулярного выражения, чтобы включить дополнительный суффикс:

+384 Critical Strike (Reforged from Parry Chance)

Что разбивается на:

enter image description here

Примечание: Мне не особо интересен дополнительный суффикс суффикса; это означает, что у меня нет требования, чтобы захватить его, хотя захват был бы удобным.

Здесь я начинаю сталкиваться с проблемами с жадным захватом. Сразу же у шаблона у меня уже есть то, что я не хочу:

  • pattern = ([%+%-]%d+) (.+)
  • value = +384
  • stat = Critical Strike (Reforged from Parry Chance)

Но попробуйте включить суффикс в шаблон:

enter image description here

с рисунком:

pattern = "([%+%-]%d+) (.+)( %(Reforged from .+%))?"

И я использую оператор ? для указания появления 0 или 1 суффикса, но который соответствует ничего.

Я слепо попытался изменить необязательную суффиксную группу из скобки ( в скобки [:

pattern = "([%+%-]%d+) (.+)[ %(Reforged from .+%)]?"

Но теперь матч снова жадный:

  • value = +384
  • stat = Critical Strike (Reforged from Parry Chance)

На основе ссылка на шаблон Lua):

  • x: (где x не является одним из магических символов ^ $()%. [] * + -?) представляет сам символ x.
  • .: (точка) представляет все символы.
  • % a: представляет все буквы.
  • % c: представляет все управляющие символы.
  • % d: отображает все цифры.
  • % l: представляет все строчные буквы.
  • % p: представляет все символы пунктуации.
  • % s: представляет все пробельные символы.
  • % u: отображает все прописные буквы.
  • % w: отображает все буквенно-цифровые символы.
  • % x: представляет все шестнадцатеричные цифры.
  • % z: представляет символ с представлением 0.
  • % x: (где x - любой не-буквенно-цифровой символ) представляет символ x. Это стандартный способ избежать волшебных персонажей. Каждому знаку пунктуации (даже не магическому) может предшествовать символ "%", когда он используется для представления себя в шаблоне.
  • [set]: представляет класс, который является объединением всех символов в наборе. Ряд символов может быть задан путем разделения концевых символов диапазона на "-". Все классы% x, описанные выше, также могут использоваться как компоненты в наборе. Все остальные символы в наборе представляют себя. Например, [% w_] (или [_% w]) представляет все буквенно-цифровые символы плюс подчеркивание, [0-7] представляет восьмеричные цифры, а [0-7% l% -] представляет восьмеричные цифры плюс нижний регистр буквы плюс символ "-". Взаимодействие между диапазонами и классами не определено. Поэтому шаблоны, такие как [% a-z] или [a - %%], не имеют никакого значения.
  • [^ set]: представляет дополнение к множеству, где set интерпретируется, как указано выше.

Для всех классов, представленных одиночными буквами (% a,% c и т.д.), соответствующая прописная буква представляет дополнение к классу. Например,% S представляет все непространственные символы.

Определения букв, пробелов и других групп символов зависят от текущей локали. В частности, класс [a-z] не может быть эквивалентен% l.

и волшебные матчи:

  • *, который соответствует 0 или более повторениям символов в классе. Эти элементы повторения всегда будут соответствовать максимально возможной последовательности;
  • +, который соответствует 1 или более повторениям символов в классе. Эти элементы повторения всегда будут соответствовать максимально возможной последовательности;
  • -, который также соответствует 0 или более повторениям символов в классе. В отличие от '*', эти элементы повторения всегда будут соответствовать кратчайшей последовательности;
  • ?, который соответствует 0 или 1 появлению символа в классе;

Я заметил, что есть жадный * и неживый модификатор -. Поскольку мой средний совпадчик строк:

(%d) (%s) (%s)

кажется, поглощает текст до конца, возможно, я должен попытаться сделать его неживым, изменив * на -:

oldPattern = "([%+%-]%d+) (.*)[ %(Reforged from .+%)]?"
newPattern = "([%+%-]%d+) (.-)[ %(Reforged from .+%)]?"

Кроме того, он не соответствует:

  • value = +384
  • stat = nil

Вместо того, чтобы средняя группа захватывала "любой" символ (т.е. .), я попробовал набор, содержащий все, кроме (:

pattern = "([%+%-]%d+) ([^%(]*)( %(Reforged from .+%))?"

и оттуда колеса сошли с фургона:

local pattern = "([%+%-]%d+) ([^%(]*)( %(Reforged from .+%))?"
local pattern = "([%+%-]%d+) ((^%()*)( %(Reforged from .+%))?"
local pattern = "([%+%-]%d+) (%a )+)[ %(Reforged from .+%)]?"

Я думал, что я был близок с:

local pattern = "([%+%-]%d+) ([%a ]+)[ %(Reforged from .+%)]?"

который фиксирует

- value = "+385"
- stat = "Critical Strike "  (notice the trailing space)

Итак, вот где я ударяю головой о подушку и ложись спать; Я не могу поверить, что провел четыре часа в этом регулярном выражении.... pattern.


@NicolBolas. Набор всех возможных строк, определенных с использованием псевдорегулярного языка выражений, следующий:

+%d %s (Reforged from %s)

где

  • + представляет либо знак плюса (+), либо "Минус-знак" (-)
  • %d представляет любой символ латинской цифры (например, 0..9)
  • %s представляет любые латинские прописные или строчные буквы или встроенные пробелы (например, A-Za-z)
  • остальные символы являются литералами.

Если бы мне пришлось писать регулярное выражение, которое явно пытается сделать то, что я хочу:

\+\-\d+ [\w\s]+( \(Reforged from [\w\s]+\))?

Но я могу дать вам практически полный список всех значений, с которыми я, вероятно, столкнусь в дикой природе, если я не объясню это достаточно хорошо.

  • +123 Parry положительное число, одно слово
  • +123 Critical Strike положительное число, два слова
  • -123 Parry отрицательное число, одно слово
  • -123 Critical Strike отрицательное число, два слова
  • +123 Parry (Reforged from Dodge) положительное число, одно слово, необязательный суффикс с одним словом
  • +123 Critical Strike (Reforged from Dodge) положительное число, два слова, необязательный суффикс с двумя словами
  • -123 Parry (Reforged from Hit Chance) отрицательное число, одно слово, необязательный суффикс, содержащий два слова
  • -123 Critical Strike (Reforged from Hit Chance) отрицательное число, два слова, необязательный суффикс с двумя словами

Есть бонус шаблоны, казалось бы, что шаблоны также будут соответствовать:

  • +1234 Critical Strike Chance четырехзначное число, три слова
  • +12345 Mount and run speed increase пятизначное число, пять слов
  • +123456 Mount and run speed increase шестизначное число, пять слов
  • -1 MoUnT aNd RuN sPeEd InCrEaSe однозначное число, пять слов
  • -1 HiT (Reforged from CrItIcAl StRiKe ChAnCe) отрицательное однозначное число, одно слово, необязательный суффикс, содержащий 3 слова

И хотя идеальный шаблон должен соответствовать вышеуказанным бонусным записям, он не должен.

Локализация

В действительности все "числа", которые я пытаюсь проанализировать, будут локализованы, например:

  • +123,456 на английском языке (en-US)
  • +123.456 в Германии (de-DE)
  • +123'456 на французском языке (fr-CA)
  • +123 456 на эстонском языке (et-EE)
  • +1,23,456 в Assamese (as-IN)

Любой ответ должен не пытаться учесть эти проблемы локализации. Вы не знаете локаль, из которой будет представлен номер, поэтому локализация номера была удалена из вопроса. Вы должны строго предполагать, что числа содержат plus sign, hyphen minus и латинские цифры 0 через 9. Я уже знаю, как разбирать локализованные числа. Этот вопрос связан с попыткой сопоставить необязательный суффикс с жадным шаблоном анализатора.

Изменить. Вам действительно не пришлось пытаться обрабатывать локализованный номер. На некотором уровне, пытаясь справиться с ними, не зная языковой стандарт, это неправильно. Например, я не включил все возможные локализации чисел. Для другого: я не знаю, какие будущие локализации могут существовать в будущем.

4b9b3361

Ответ 1

Хмм, у меня нет Lua4, но этот шаблон работает под Lua5. Я ожидаю, что он тоже будет работать для Lua4.

Обновление 1. Поскольку были указаны дополнительные требования (локализация), я адаптировал шаблон и тесты для их отражения.

Обновление 2. Обновлен шаблон и тесты для обработки дополнительного класса текста, содержащего номер, указанный в комментариях в комментарии @IanBoyd. Добавлено объяснение шаблона строки.

Обновление 3: добавлено изменение для случая, когда локализованное число рассматривается отдельно, как указано в последнем обновлении вопроса.

Try:

"(([%+%-][',%.%d%s]-[%d]+)%s*([%a]+[^%(^%)]+[%a]+)%s*(%(?[%a%s]*%)?))"

или (не пытайтесь проверить маркеры локализации номера) - просто возьмите все, что не является буквой с цифрой дозорной точки в конце шаблона:

"(([%+%-][^%a]-[%d]+)%s*([%a]+[^%(^%)]+[%a]+)%s*(%(?[%a%s]*%)?))"

Ни один из вышеприведенных шаблонов не предназначен для обозначения чисел в научной нотации (например: 1.23e + 10)

Lua5 test (Отредактировано для очистки - тесты становятся загроможденными):

function test(tab, pattern)
   for i,v in ipairs(tab) do
     local f1, f2, f3, f4 = v:match(pattern)
     print(string.format("Test{%d} - Whole:{%s}\nFirst:{%s}\nSecond:{%s}\nThird:{%s}\n",i, f1, f2, f3, f4))
   end
 end

 local pattern = "(([%+%-][',%.%d%s]-[%d]+)%s*([%a]+[^%(^%)]+[%a]+)%s*(%(?[%a%s]*%)?))"
 local testing = {"+123 Parry",
   "+123 Critical Strike",
   "-123 Parry",
   "-123 Critical Strike",
   "+123 Parry (Reforged from Dodge)",
   "+123 Critical Strike (Reforged from Dodge)",
   "-123 Parry (Reforged from Hit Chance)",
   "-123 Critical Strike (Reforged from Hit Chance)",
   "+122384    Critical    Strike      (Reforged from parry chance)",
   "+384 Critical Strike ",
   "+384Critical Strike (Reforged from parry chance)",
   "+1234 Critical Strike Chance (Reforged from CrItIcAl StRiKe ChAnCe)",
   "+12345 Mount and run speed increase (Reforged from CrItIcAl StRiKe ChAnCe)",
   "+123456 Mount and run speed increase (Reforged from CrItIcAl StRiKe ChAnCe)",
   "-1 MoUnT aNd RuN sPeEd InCrEaSe (Reforged from CrItIcAl StRiKe ChAnCe)",
   "-1 HiT (Reforged from CrItIcAl StRiKe ChAnCe)",
   "+123,456 +1234 Critical Strike Chance (Reforged from CrItIcAl StRiKe ChAnCe)",
   "+123.456 Critical Strike Chance (Reforged from CrItIcAl StRiKe ChAnCe)",
   "+123'456 Critical Strike Chance (Reforged from CrItIcAl StRiKe ChAnCe)",
   "+123 456 Critical Strike Chance (Reforged from CrItIcAl StRiKe ChAnCe)",
   "+1,23,456 Critical Strike Chance (Reforged from CrItIcAl StRiKe ChAnCe)",
   "+9 mana every 5 sec",
   "-9 mana every 20 min (Does not occurr in data but gets captured if there)"}
 test(testing, pattern)

Здесь пробой шаблона:

local explainPattern =  
   "(" -- start whole string capture
   ..
   --[[
   capture localized number with sign - 
   take at first as few digits and separators as you can 
   ensuring the capture ends with at least 1 digit
   (the last digit is our sentinel enforcing the boundary)]]
   "([%+%-][',%.%d%s]-[%d]+)" 
   ..
   --[[
   gobble as much space as you can]]
   "%s*"
   ..
   --[[
   capture start with letters, followed by anything which is not a bracket 
   ending with at least 1 letter]]
   "([%a]+[^%(^%)]+[%a]+)"
   ..
   --[[
   gobble as much space as you can]]
   "%s*"
   ..
   --[[
   capture an optional bracket
   followed by 0 or more letters and spaces
   ending with an optional bracket]]
   "(%(?[%a%s]*%)?)"
   .. 
   ")" -- end whole string capture

Ответ 2

Зачем анализировать это в одном шаблоне, если вы можете использовать несколько?

Сначала введите номер:

local num, rest = string.match(test_string, "([%+%-]?%d+)%S*(.+)")

Затем создайте таблицу, которая перечисляет возможности для типа нажатия.

local hitTypes =
{
  "Hit",
  "Critical Strike",
  -- Insert more.
}

Теперь перебираем список по списку, проверяя его.

local hitIndex = nil
local reforge = nil

for i, htype in ipairs(hitTypes) do
  local final = string.match(rest, htype .. "%S*(.*)")
  if(final) then
    hitIndex = i
    reforge = string.match(final, "%(Reforged from (.+)%)")
  end
end

Модели Lua ограничены, поэтому лучше использовать фактический код, чтобы избежать их ограничений.

Ответ 3

function match_some_stat_thing(str)
    local sign, amount, label, note = string.match(str.."()", "^([%+%-])(%d+) ([%a ]-) ?(%b())")
    return sign == "+" and amount or -amount, label, string.match(note, "%((.*)%)")
end
print(string.format("%d %q %q", match_some_stat_thing("+384 Critical Strike (Reforged from Parry Chance)")))
print(string.format("%d %q %q", match_some_stat_thing("+384 Critical Strike")))
print(string.format("%d %q %q", match_some_stat_thing("+384 Critical Strike ")))

Не один шаблон, но он работает.