Lua中的贪婪/非贪婪模式匹配和可选后缀

Ian*_*oyd 14 lua

在Lua中,我正在尝试模式匹配和捕获:

+384 Critical Strike (Reforged from Parry Chance)
Run Code Online (Sandbox Code Playgroud)

(+384) (Critical Strike)
Run Code Online (Sandbox Code Playgroud)

后缀(Reforged from %s)是可选的.

长版

我正在尝试使用模式匹配Lua中的字符串(即)strfind

注意:在Lua中,他们不会将它们称为正则表达式,他们称之为模式,因为它们不是常规的.

示例字符串:

+384 Critical Strike
+1128 Hit
Run Code Online (Sandbox Code Playgroud)

这分为两部分我想捕获:

在此输入图像描述

  • 数量,与领先的正或负的指示符; 在他的情况下是+384
  • 字符串,在这种情况下Critical Strike.

我可以使用一个相当简单的模式捕获它们:

在此输入图像描述

而这种模式在lua中起作用:

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

棘手的部分

现在我需要扩展该正则表达式 模式以包含可选后缀:

+384 Critical Strike (Reforged from Parry Chance)
Run Code Online (Sandbox Code Playgroud)

其中细分为:

在此输入图像描述

注意:我并不特别关心可选的尾随后缀; 这意味着我没有要求捕获它,虽然捕获它会很方便.

这是我开始陷入贪婪捕获问题的地方.我已经拥有的模式就是我不想要的:

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

但是,让我们尝试在模式中包含后缀:

在此输入图像描述

与模式:

pattern = "([%+%-]%d+) (.+)( %(Reforged from .+%))?"
Run Code Online (Sandbox Code Playgroud)

我正在使用?运算符来指示01出现后缀,但这些都没有匹配.

盲目地尝试将可选后缀组从括号更改(为括号[:

pattern = "([%+%-]%d+) (.+)[ %(Reforged from .+%)]?"
Run Code Online (Sandbox Code Playgroud)

但现在比赛再次贪婪:

  • 值= +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% - ]表示八进制数字加上小写字母字母加上' - '字符.范围和类之间的交互未定义.因此,[%az]或[a - %%]等模式没有意义.
  • [^ set]:表示set的补码,其中set如上所述.

对于由单个字母(%a,%c等)表示的所有类,相应的大写字母表示类的补码.例如,%S代表所有非空格字符.

字母,空格和其他字符组的定义取决于当前区域设置.特别是,类[az]可能不等于%l.

和魔法匹配者:

  • *,匹配类中0或更多重复的字符.这些重复项总是匹配最长的序列;
  • +,匹配类中一个或多个重复的字符.这些重复项总是匹配最长的序列;
  • -,它也匹配类中0或更多重复的字符.与'*'不同,这些重复项总是匹配最短的序列;
  • ?,匹配类中0或1次出现的字符;

我注意到有一个贪婪的 *,非贪婪的 -修饰符.由于我的中间字符串匹配器:

(%d) (%s) (%s)
Run Code Online (Sandbox Code Playgroud)

似乎是在吸收文字直到最后,也许我应该试着让它变得非贪婪,通过改变*-:

oldPattern = "([%+%-]%d+) (.*)[ %(Reforged from .+%)]?"
newPattern = "([%+%-]%d+) (.-)[ %(Reforged from .+%)]?"
Run Code Online (Sandbox Code Playgroud)

除了现在它不匹配:

  • 值= +384
  • stat = nil

而不是中间组捕获"任何"字符(即.),我尝试了一个包含所有内容的集合,除了 (:

pattern = "([%+%-]%d+) ([^%(]*)( %(Reforged from .+%))?"
Run Code Online (Sandbox Code Playgroud)

从那里车轮从货车上掉下来:

local pattern = "([%+%-]%d+) ([^%(]*)( %(Reforged from .+%))?"
local pattern = "([%+%-]%d+) ((^%()*)( %(Reforged from .+%))?"
local pattern = "([%+%-]%d+) (%a )+)[ %(Reforged from .+%)]?"
Run Code Online (Sandbox Code Playgroud)

我以为我很接近:

local pattern = "([%+%-]%d+) ([%a ]+)[ %(Reforged from .+%)]?"
Run Code Online (Sandbox Code Playgroud)

抓住了

- value = "+385"
- stat = "Critical Strike "  (notice the trailing space)
Run Code Online (Sandbox Code Playgroud)

所以这就是我把头靠在枕头上去睡觉的地方; 我不敢相信我已经花了四个小时在这个正则表达式模式上.


@NicolBolas使用伪正则表达式语言定义的所有可能字符串的集合是:

+%d %s (Reforged from %s)
Run Code Online (Sandbox Code Playgroud)

哪里

  • +表示加号(+)"减号"(-)
  • %d代表任何拉丁数字字符(例如0..9)
  • %s表示任何拉丁大写或小写字母或嵌入空格(例如A-Za-z)
  • 剩下的字符是文字​​.

如果我必须写一个显然试图做我想要的正则表达式:

\+\-\d+ [\w\s]+( \(Reforged from [\w\s]+\))?
Run Code Online (Sandbox Code Playgroud)

但是如果我不能很好地解释它,我可以给你几乎完整的所有价值清单,我可能会在野外遇到这些价值.

  • +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 在阿萨姆语(as-IN)

任何答案都不得试图解释这些本地化问题.您不知道将显示数字的区域设置,这就是数字本地化已从问题中删除的原因.你必须严格假设的数字包含plus sign,hyphen minus和拉丁美洲的数字0通过9.我已经知道如何解析本地化的数字了.这个问题是关于尝试将可选后缀与贪婪模式解析器匹配.

编辑:你真的没有尝试处理本地化的数字.在某种程度上试图处理它们而不知道语言环境是错误的.例如,我没有包括所有可能的数字本地化.另一方面:我不知道未来可能存在哪些未来的本地化.

Ant*_*ill 7

嗯,我没有安装Lua4,但这种模式在Lua5下工作.我希望它也适用于Lua4.

更新1:由于已经指定了其他要求(本地化),我已经调整了模式和测试以反映这些要求.

更新2:更新了模式和测试,以处理包含@IanBoyd在评论中提到的数字的其他类文本.添加了字符串模式的说明.

更新3:为问题的上次更新中提到的单独处理本地化数字的情况添加了变体.

尝试:

"(([%+%-][',%.%d%s]-[%d]+)%s*([%a]+[^%(^%)]+[%a]+)%s*(%(?[%a%s]*%)?))"
Run Code Online (Sandbox Code Playgroud)

或者(不尝试验证号码本地化令牌) - 只需在模式的末尾处取出不带字母数字的字母:

"(([%+%-][^%a]-[%d]+)%s*([%a]+[^%(^%)]+[%a]+)%s*(%(?[%a%s]*%)?))"
Run Code Online (Sandbox Code Playgroud)

上述两种模式都不是为了处理科学记数法中的数字(例如:1.23e + 10)

Lua5测试(编辑清理 - 测试变得混乱):

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)
Run Code Online (Sandbox Code Playgroud)

这是模式的细分:

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
Run Code Online (Sandbox Code Playgroud)