[英]Complex parsing of a string in Python
我想解析这样的格式的字符串:
[{text1}]{quantity}[{text2}]
此规则意味着,一开始会有一些文本可以有选择地存在或不存在,其后跟一个{quantity},其语法在下面进行介绍,后面是更多可选的文本。
{quantity}可以采用多种形式,其中{n}是任何正整数
{n}
{n}PCS
{n}PC
{n}PCS.
{n}PC.
Lot of {n}
另外,它应接受以下附加规则:
{n} {text2}
在此规则中,{n}后跟一个空格,然后是{text2}
在出现PC或PCS的情况下
期望的输出被归一化为两个变量:
如果{quantity}除正整数之外还包含其他任何内容,则{n}仅由整数组成,并且{n}的其余部分(例如“ PCS。”)将从{n}和结果文本字符串中剥离。
在文本部分中,可能会出现更多的整数。 除找到的{quantity}外,任何其他内容均应视为文本的一部分,而不应解释为其他数量。
我是前C / C ++程序员。 如果必须使用这些语言解决此问题,则可能会在lex和yacc中使用规则,否则我将不得不编写很多讨厌的代码来手动解析它。
我想学习一种干净的方法来用Python有效地对此进行编码,可能使用某种形式的规则轻松支持更多情况。 我想我可以在Python中使用lex和yacc,但是我想知道是否有更简单的方法。 我是Python新手; 我什至不知道从哪里开始。
我并没有要求任何人为完整的解决方案编写代码,而是需要一种或两种方法,也许还需要一些示例代码来说明如何实现。
通过Pyparsing,您可以使用'+'和'|'将较小的解析器拼接在一起,从而构建一个解析器 运算符(以及其他)。 您还可以将名称附加到解析器中的各个元素,以使以后更容易获得这些值。
from pyparsing import (pyparsing_common, CaselessKeyword, Optional, ungroup, restOfLine,
oneOf, SkipTo)
int_qty = pyparsing_common.integer
# compose an expression for the quantity, in its various forms
"""
{n}
{n}PCS
{n}PC
{n}PCS.
{n}PC.
Lot of {n}
"""
LOT = CaselessKeyword("lot")
OF = CaselessKeyword("of")
pieces = oneOf("PC PCS PC. PCS.", caseless=True)
qty_expr = Optional(LOT + OF).suppress() + int_qty("qty") + Optional(pieces).suppress()
# compose expression for entire line
line_expr = SkipTo(qty_expr)("text1") + qty_expr + restOfLine("text2")
tests = """
Send me 1000 widgets pronto!
Deliver a Lot of 50 barrels of maple syrup by Monday, June 10.
My shipment was short by 25 pcs.
"""
line_expr.runTests(tests)
打印:
Send me 1000 widgets pronto!
['Send me', 1000, ' widgets pronto!']
- qty: 1000
- text1: ['Send me']
- text2: widgets pronto!
Deliver a Lot of 50 barrels of maple syrup by Monday, June 10.
['Deliver a ', 50, ' barrels of maple syrup by Monday, June 10.']
- qty: 50
- text1: ['Deliver a ']
- text2: barrels of maple syrup by Monday, June 10.
My shipment was short by 25 pcs.
['My shipment was short by', 25, '']
- qty: 25
- text1: ['My shipment was short by']
- text2:
编辑:Pyparsing支持两种形式的替代匹配:MatchFirst,它停在第一个匹配替代上(使用'|'运算符定义),或者Or,评估所有替代并选择最长的匹配(使用'^'定义)运营商)。 因此,如果需要数量表达式的优先级,则可以明确定义它:
qty_pcs_expr = int_qty("qty") + White().suppress() + pieces.suppress()
qty_expr = Optional(LOT + OF).suppress() + int_qty("qty") + FollowedBy(White())
# compose expression for entire line
line_expr = (SkipTo(qty_pcs_expr)("text1") + qty_pcs_expr + restOfLine("text2") |
SkipTo(qty_expr)("text1") + qty_expr + restOfLine("text2"))
这是新的测试:
tests = """
Send me 1000 widgets pronto!
Deliver a Lot of 50 barrels of maple syrup by Monday, June 10.
My shipment was short by 25 pcs.
2. I expect 22 pcs delivered in the morning
On May 15 please deliver 1000 PCS.
"""
赠送:
2. I expect 22 pcs delivered in the morning
['2. I expect ', 22, ' delivered in the morning']
- qty: 22
- text1: ['2. I expect ']
- text2: delivered in the morning
On May 15 please deliver 1000 PCS.
['On May 15 please deliver ', 1000, '']
- qty: 1000
- text1: ['On May 15 please deliver ']
- text2:
我不知道您是否要使用re
,但是这是我认为可行的正则表达式。 您可以更改str
值进行测试。 匹配返回一个具有三个值[{text1}] {quantity} [{text2}]的元组。 如果缺少text1和text2,则元组中的第一和最后一项将为空。
import re
str = "aSOETIHSIBSROG1PCS.ecsrGIR"
matchObj = re.search(r'([a-zA-Z]+|)(\dPCS?\.?|Lot of \d)([a-zA-Z]+|)',str).groups()
print matchObj.groups()
#Output
('aSOETIHSIBSROG', '1PCS.', 'ecsrGIR')
这是一个使用正则表达式匹配两种情况的规则处理器。 我创建了一个自定义匹配结果类,以保存从输入字符串中提取的相关值。 规则处理器连续尝试以下规则:
运行时,导致
abc 23 PCS. def
amount=23 qtype=PCS. text1="abc" text2="def" rule=1
abc 23pc def
amount=23 qtype=pc text1="abc" text2="def" rule=1
abc 24pc.def
amount=24 qtype=pc. text1="abc" text2="def" rule=1
abc 24 pcs def
amount=24 qtype=pcs text1="abc" text2="def" rule=1
abc lot of 24 def
amount=24 qtype=lot of text1="abc" text2="def" rule=2
3 abcs
amount=3 qtype=None text1="" text2="abcs" rule=3
import re class Match: def __init__(self, amount, qtype, text1, text2, rule): self.amount = int(amount) self.qtype = qtype self.text1 = text1 self.text2 = text2 self.rule = rule def __str__(self): return 'amount={} qtype={} text1="{}" text2="{}" rule={}'.format( self.amount, self.qtype, self.text1, self.text2, self.rule) #{n} pc pc. pcs pcs. def rule1(s): m = re.search("\s*(?P\d+)\s*(?PPCS?\.?)\s*", s, re.IGNORECASE) if m: return Match(m.group('amount'), m.group('qtype'), text1=s[:m.start()], text2=s[m.end():], rule=1) return None #lot of {n} def rule2(s): m = re.search("\s*lot of\s*(?P\d+)\s*", s, re.IGNORECASE) if m: return Match(m.group('amount'), 'lot of', text1=s[:m.start()], text2=s[m.end():], rule=2) return None #{n} {text2} def rule3(s): m = re.search("\s*(?P\d+)\s*",s) if m: return Match(m.group('amount'), None, text1=s[:m.start()], text2=s[m.end():], rule=3) return None RULES = [rule1, rule2, rule3] def process(s): for rule in RULES: m = rule(s) if m: return m return None tests = [ "abc 23 PCS. def", "abc 23pc def", "abc 24pc.def", "abc 24 pcs def", "abc lot of 24 def", "3 abcs" ] for t in tests: m = process(t) print(t) print(m)
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.