我正在构建一个简单的解析器,它接受如下查询: 'show fizi承诺从2010年1月1日到11月2日' 到目前为止,我有:
class QueryParser(object):
def parser(self, stmnt):
keywords = ["select", "from","to", "show","commits", "where", "group by", "order by", "and", "or"]
[select, _from, _to, show, commits, where, groupby, orderby, _and, _or] = [ CaselessKeyword(word) for word in keywords ]
user = Word(alphas+"."+alphas)
user2 = Combine(user + "'s")
startdate=self.getdate()
enddate=self.getdate()
bnf = (show|select)+(user|user2).setResultsName("user")+(commits).setResultsName("stats")\
+Optional(_from+startdate.setResultsName("start")+_to+enddate.setResultsName("end"))
a = bnf.parseString(stmnt)
return a
def getdate(self):
integer = Word(nums).setParseAction(lambda t: int(t[0]))
date = Combine(integer('year') + '/' + integer('month') + '/' + integer('day'))
#date.setParseAction(self.convertToDatetime)
return date
我希望日期更通用。意思是用户可以提供2010年1月20日或其他一些日期格式。我找到了一个在线解析的好日期。它将日期作为字符串,然后解析它。所以我剩下的就是从我的解析器中获取日期字符串。如何进行标记化和捕获两个日期字符串。目前它只捕获格式'y / m / d'格式。有没有办法让整个字符串无论如何格式化。在关键字和关键字之后立即捕获单词。非常感谢任何帮助。
答案 0 :(得分:2)
一种简单的方法是要求引用日期。一个粗略的例子是这样的,但如果需要,你需要调整以适应你当前的语法:
from pyparsing import CaselessKeyword, quotedString, removeQuotes
from dateutil.parser import parse as parse_date
dp = (
CaselessKeyword('from') + quotedString.setParseAction(removeQuotes)('from') +
CaselessKeyword('to') + quotedString.setParseAction(removeQuotes)('to')
)
res = dp.parseString('from "jan 20" to "apr 5"')
from_date = parse_date(res['from'])
to_date = parse_date(res['to'])
# from_date, to_date == (datetime.datetime(2015, 1, 20, 0, 0), datetime.datetime(2015, 4, 5, 0, 0))
答案 1 :(得分:1)
我建议使用类似sqlparse的东西来处理所有奇怪的边缘情况。如果你不得不处理更高级的案例,那么从长远来看这可能是一个更好的选择。
编辑:为什么不将日期块解析为字符串?像这样:
来自pyparsing import CaselessKeyword,Word,Combine,Optional,alphas,nums
class QueryParser(object):
def parser(self, stmnt):
keywords = ["select", "from", "to", "show", "commits", "where",
"groupby", "order by", "and", "or"]
[select, _from, _to, show, commits, where, groupby, orderby, _and, _or]\
= [CaselessKeyword(word) for word in keywords]
user = Word(alphas + "." + alphas)
user2 = Combine(user + "'s")
startdate = Word(alphas + nums + "/")
enddate = Word(alphas + nums + "/")
bnf = (
(show | select) + (user | user2).setResultsName("user") +
(commits).setResultsName("stats") +
Optional(
_from + startdate.setResultsName("start") +
_to + enddate.setResultsName("end"))
)
a = bnf.parseString(stmnt)
return a
这给了我类似的东西:
In [3]: q.parser("show fizi commits from 1/1/2010 to 11/2/2006")
Out[3]: (['show', 'fizi', 'commits', 'from', '1/1/2010', 'to', '11/2/2006'], {'start': [('1/1/2010', 4)], 'end': [('11/2/2006', 6)], 'stats': [('commits', 2)], 'user': [('fizi', 1)]})
答案 2 :(得分:1)
您可以使pyparsing解析器在匹配的内容中非常宽松,然后使用解析操作执行更严格的值检查。如果您的日期字符串都是非空白字符,这一点尤其容易。
例如,假设我们想要解析一个月的名称,但出于某种原因,我们不希望我们的解析器表达式只执行`oneOf(' 1月2月3月......等等)。我们可以放置一个占位符,它只会将一组Word字符解析为下一个不符合条件的字符(空格或标点符号)。
monthName = Word(alphas.upper(), alphas.lower())
所以这里我们的月份以大写字母开头,后跟0或更多小写字母。显然这将匹配许多非月份名称,因此我们将添加一个解析操作来进行额外的验证:
def validate_month(tokens):
import calendar
monthname = tokens[0]
print "check if %s is a valid month name" % monthname
if monthname not in calendar.month_name:
raise ParseException(monthname + " is not a valid month abbreviation")
monthName.setParseAction(validate_month)
如果我们这两个陈述:
print monthName.parseString("January")
print monthName.parseString("Foo")
我们得到了
check if January is a valid month name
['January']
check if Foo is a valid month name
Traceback (most recent call last):
File "dd.py", line 15, in <module>
print monthName.parseString("Foo")
File "c:\python27\lib\site-packages\pyparsing.py", line 1125, in parseString
raise exc
pyparsing.ParseException: Foo is not a valid month abbreviation (at char 0), (line:1, col:1)
(完成测试后,您可以从解析操作的中间删除print语句 - 我只是将其包含在内,以表明它在解析过程中被调用。)
如果您可以使用以空格分隔的日期格式,那么您可以将解析器编写为:
date = Word(nums,nums+'/-')
然后您可以接受1/1/2001
,29-10-1929
等等。同样,您还将匹配32237--/234//234/7
之类的字符串,显然不是有效日期,因此您可以编写验证解析操作来检查字符串的有效性。在解析操作中,您可以实现自己的验证逻辑,或者调用外部库。 (如果你能够容忍不同的语言环境,那么你必须要警惕日期,例如&#39; 4/3/2013&#39;因为月份优先和日期优先选项有很多种,这个字符串可以很容易意味着4月3日或3月4日。)你也可以让解析操作为你做实际的转换,这样当你处理解析的标记时,字符串将是一个实际的Python日期时间。