Python中

时间:2016-06-15 19:45:01

标签: python string parsing

我想用这样的格式解析字符串:

[{text1}]{quantity}[{text2}]

此规则意味着在开头有一些文本可以选择在那里,然后是{quantity},其语法我在下面描述,然后是更多可选文本。

{quantity}可以采用多种形式,{n}为任何正整数

{n}
{n}PCS
{n}PC
{n}PCS.
{n}PC.
Lot of {n}

此外,它应该接受这个附加规则:

{n} {text2} 

在此规则中,{n}后跟一个空格,然后是{text2}

出现PC或PCS的情况

  • 它可能会或可能不会跟着点
  • 不区分大小写
  • 空格可以选择出现在{n}和PCS
  • 之间
  • 以下均已剥离:PC或PCS,可选点和可选空间

将所需输出标准化为两个变量:

  • {n}为整数
  • [{text1}] [{text2}],即第一个{text1}(如果存在),然后是空格,然后是{text2}(如果存在),连接到一个字符串。仅当有两个文本片段时,才会使用分隔文本片段的空间。

如果{quantity}包含除正整数之外的任何内容,则{n}仅包含整数,其余的{quantity}(例如" PCS。")将从{ n}和结果文本字符串。

在文本部分中,可能会出现更多整数。除了{quantity}之外的其他任何内容都应该被视为文本的一部分,而不是被解释为另一个数量。

我是前C / C ++程序员。如果我不得不用这些语言来解决这个问题,我可能会在lex和yacc中使用规则,否则我将不得不编写很多讨厌的代码来手工解析它。

我想学习一种在Python中有效编码的简洁方法,可能使用某种形式的规则来轻松支持更多案例。我想我可以在Python中使用lex和yacc,但我想知道是否有更简单的方法。我是一个Python新手;我甚至不知道从哪里开始。

我不是要求任何人为完整的解决方案编写代码,而是我需要一两种方法,也许还有一些示例代码显示了如何执行此操作。

3 个答案:

答案 0 :(得分:2)

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: 

答案 1 :(得分:1)

我不知道你是否想使用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')

答案 2 :(得分:0)

这是一个规则处理器,使用正则表达式匹配您的两个案例。我创建一个自定义匹配结果类来保存输入字符串中的相关提取值。规则处理器连续尝试以下规则:

  • rule1 - 尝试匹配{n},后跟pc,pc。,pcs或pcs。
  • rule2 - 尝试匹配{n}前缀为"很多"
  • rule3 - 匹配{n}后跟{text2}

运行时,结果

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)