我目前正在制作扑克手历史解析器,作为我的单身汉项目的一部分。我在过去的几天里一直在做一些研究,并且遇到了一些不错的解析器生成器(我选择了JavaCC,因为项目本身将用Java编码)。
尽管手历史语法非常基本且直截了当,但由于玩家昵称中允许的字符集,存在歧义问题。
假设我们有以下格式的一行:
Seat 5: myNickname (1500 in chips)
令牌myNickname
可以包含任何字符以及空格。这意味着,(1500 in chip
和Seat 5:
都是有效的昵称 - 这最终会导致歧义问题。除了长度(4-12个字符)之外,对玩家的昵称没有限制。
我需要解析和存储几个数据以及玩家的昵称(例如在这种特殊情况下的座位位置和筹码数量),所以我的问题是,我有什么选择?
我很乐意使用JavaCC来做这件事:
SeatRecord seat() :
{ Token seatPos, nickname, chipStack; }
{
"Seat" seatPos=<INTEGER> ":" nickname=<NICKNAME> "(" chipStack=<INTEGER>
"in chips)"
{
return new SeatRecord(seatPos.image, nickname.image, chipStack.image);
}
}
现在哪个不起作用(由于上述问题)
我还搜索了GLR解析器(显然处理了暧昧的语法) - 但是除了Bison之外,它们似乎大部分被遗弃或记录不清,但是它不支持Java的GLR解析器,并且可能过于复杂离开工作(除了歧义问题,语法本身是非常基本的,正如我所提到的)
或者我应该坚持自己对字符串进行标记,并使用indexOf(), lastIndexOf()
等来解析我需要的数据?只有当它是剩下的唯一选择时我才会这样做,因为它太丑了恕我直言,我可能会错过一些情况(这会导致错误的解析)
答案 0 :(得分:7)
如果输入格式与指定的一样简单,则可以使用简单的正则表达式:
^Seat ([0-9]+): (.*) \(([0-9]+) in chips\)$
在这种情况下,正则表达式引擎的NFA解决了您的歧义,括号是捕获组,以便您可以提取您感兴趣的信息。
答案 1 :(得分:2)
您有两种解决方案:
无论如何,我认为原始设计存在问题。昵称不应该允许这样的一组名称。此外,为什么不能使用标识符而不是名称 - 名称可以存储在数据库中。
答案 2 :(得分:2)
您系统的语法可能如下所示(写成无上下文语法):
S -> seating nickname chips
seating -> "Seat " number ":"
number -> "0" | "1" | "2" | "3" | "4" | "5" | "6" | "7" | "8" | "9"
number -> number number
nickname -> "a" | "b" | "c" ...... | "z" | ...."+" | "?" | number
nickname -> nickname nickname
chips -> "(" number "in chips)"
注意表格的规则:
number -> number number
这基本上允许无限的语法。注意&#34;无限语法&#34;并不意味着你封装了一切。上面的行基本上等同于正则表达式(\d*)
。
我发现在CFG中输入语法然后将其转换为常规语法可以帮助我大部分时间。更多关于如何做到这一点here。祝你好运!