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

时间:2012-11-29 05:06:27

标签: lua

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

+384 Critical Strike (Reforged from Parry Chance)

作为

(+384) (Critical Strike)

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

长版

我试图匹配Lua using patterns (i.e. strfind)

中的字符串
  

注意:在Lua中,他们不会将其称为正则表达式,他们称之为模式,因为他们不是regular

示例字符串:

+384 Critical Strike
+1128 Hit

这分为两部分我想捕捉:

enter image description here

  • ,带有正面或负面指标;他的案例是+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

棘手的部分

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

+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 .+%))?"

我正在使用?运算符来指示后缀的01出现,但匹配

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

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

但现在比赛再次贪婪:

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

基于Lua pattern reference):

  
      
  • 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的补码,其中set的解释如上所述。
  •   
     

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

     

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

和魔法匹配者:

  • * ,匹配班级中0个或更多重复的字符。这些重复项始终与最长的序列匹配;
  • + ,匹配班级中一个或多个重复的字符。这些重复项始终与最长的序列匹配;
  • - ,它还匹配班级中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)

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


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

+%d %s (Reforged from %s)

,其中

  • + 代表Plus Sign (+)"Minus Sign" (-)
  • %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在阿萨姆语(as-IN)

任何答案必须尝试解释这些本地化问题。您不知道将显示数字的区域设置,这就是数字本地化已从问题中删除的原因。您必须严格认为这些数字包含plus signhyphen minus和拉丁数09。我已经知道如何解析本地化的数字了。这个问题是关于尝试将可选后缀与贪婪模式解析器匹配。

修改:您确实没有必要尝试处理本地化号码。在某种程度上试图处理它们而不知道语言环境是错误的。例如,我没有包括所有可能的数字本地化。另一方面:我不知道将来可能存在哪些本地化。

4 个答案:

答案 0 :(得分:7)

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

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

更新2 :更新了模式和测试,以便在评论中处理包含@IanBoyd所述号码的其他类文本。添加了解释 字符串模式。

更新3 :针对问题的上次更新中提到的单独处理本地化号码的情况添加了变体。

尝试:

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

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

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

上述两种模式都不是用科学记数法来处理数字(例如: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)

以下是模式的细分:

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

答案 1 :(得分:1)

为什么在使用多个模式时,可以在一个模式中解析它?

首先,获取数字:

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模式是有限的,因此最好使用实际代码来避免它们的限制。

答案 2 :(得分:1)

这要简单得多,您不仅可以直接匹配模式,还可以直接寻找所需字符串的简短输出,可以使用string.gsub

示例:

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)"}

for k,v in ipairs(testing) do
  local result = string.gsub(v, "([%+%-][',%.%d%s]-[%+%d]+)%s*([%a]+[^%(^%)]+[%a]+)%s*(%(?[%a%s]*%)?)", '(%1) (%2) %3')
  print(result)
end

输出

(+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) 
(+384) (Critical 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)

答案 3 :(得分:0)

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 ")))

不是单一模式,但它有效。