Python regex - 字符串中的可选字段

Posted

技术标签:

【中文标题】Python regex - 字符串中的可选字段【英文标题】:Python regex - Optional fields in a string 【发布时间】:2015-05-21 04:54:01 【问题描述】:

我正在尝试自学 Python,但我对解析概念还是很陌生。我正在尝试解析我的消防寻呼机的输出,它似乎遵循如下一致的模式:

(UNIT1, UNIT2, UNIT3) 911-STRU (Box# 12345) aBusiness 12345 Street aTown (Xstr CrossStreet1/CrossStreet2) building fire, persons reported #F123456

似乎每个部分都使用()括号分隔,字段分解如下

(Responded trucks) CallSource-JobClassification (Box number if available) Building Name, Building Address (Cross streets) Description of job #JobNumber

废话,写这篇文章的时候刚接到一个电话。如果没有提供箱号,则该部分将被完全跳过,这意味着它直接进入地址部分,因此我不能指望使用括号进行解析。

所以对于那里的解析专家,我可以用 pyparsing 来攻击它还是需要一个自定义解析器?此外,我可以使用解析器定位特定部分,因此它们出现的顺序无关紧要,就像 Box# 是可选字段一样?

我的目标是获取这些输入,通过解析对其进行整理,然后通过 Twitter、SMS、电子邮件或以上所有方式发送。

在此先感谢

编辑:

我已经使用以下代码完成了这 99% 的工作

import re

sInput = ('(UNIT123, UNIT1234) AMB-MED APPLE HEADQUARTERS 1 INFINITE LOOP CUPERTINO. (XStr DE ANZA BLVD/MARIANI AVE) .42YOM CARDIAC ARREST. #F9876543')

#sInput = '(UNIT123, UNIT1234) ALARM-SPRNKLR (Alarm Type MANUAL/SMOKE) (Box 12345) APPLE HEADQUARTERS 1 INFINITE LOOP CUPERTINO. (XStr DE ANZA BLVD/MARIANI AVE) #F9876544'

# Matches truck names using the consistent four uppercase letters followed by three - four numbers.
pAppliances = re.findall(r'\w[A-Z]3\d[0-9]2,3', sInput)

# Matches source and job type using the - as a guide, this section is always proceeded by the trucks on the job
# therefore is always proceeded by a ) and a space. Allows between 3-9 characters either side of the - this is
# to allow such variations as 911-RESC, FAA-AIRCRAFT etc.
pJobSource = re.findall(r'\) ([A-Za-z1-9]2,8-[A-Za-z1-9]2,8)', sInput)

# Gets address by starting at (but ignoring) the job source e.g. -RESC and capturing everything until the next . period
# the end of the address section always has a period. Uses ?; to ignore up to two sets of brackets that may appear in
# the string for things such as box numbers or alarm types.

pAddress = re.findall(r'-[A-Z1-9]2,8 (.*?)\. \(', sInput)
pAddressOptionTwo = re.findall(r'-[A-Z1-9]2,8(?: \(.*?\))(?: \(.*?\)) (.*?)\. \(', sInput)

# Finds the specified cross streets as they are always within () brackets, each bracket has a space immediately
# before or after and the work XStr is always present.
pCrossStreet = re.findall(r' \((XStr.*?)\) ', sInput)

# The job details / description is always contained between two . periods e.g.  .42YOM CARDIAC ARREST.  each period
# has a space either immediately before or after.
pJobDetails = re.findall(r' \.(.*?)\. ', sInput)

# Job number is always in the format #F followed by seven digits.  The # is always proceeded by a space.  Allowed
# between 1 and 8 digits for future proofing.
pJobNumber = re.findall(r' (#F\d0,7)', sInput)

print pAppliances
print pJobSource
print pAddress
print pCrossStreet
print pJobDetails
print pJobNumber

在未注释的 sInput 字符串上运行时,它会返回以下内容

['UNIT123', 'UNIT1234']
['AMB-MED']
['APPLE HEADQUARTERS 1 INFINITE LOOP CUPERTINO']
['XStr DE ANZA BLVD/MARIANI AVE']
['42YOM CARDIAC ARREST']
['#F9876543']

但是,当我在注释的 sInput 字符串上运行它时,我得到以下内容

['UNIT123', 'UNIT1234']
['ALARM-SPRNKLR']
['(Alarm Type MANUAL/SMOKE) (Box 12345) APPLE HEADQUARTERS 1 INFINITE LOOP CUPERTINO']
['XStr DE ANZA BLVD/MARIANI AVE']
[]
['#F9876544']

这是因为此消息中包含了两个选项括号集。我设法使用 pAddressOptionTwo 行更正了这个问题,但是当应用第一个字符串时,它根本不返回地址,因为它没有找到括号。

所以新的重新聚焦的问题是:

如何在正则表达式行中添加可选参数。如果存在括号,则忽略它们及其内容并返回字符串的其余部分,或者如果不存在括号,则照常继续。

【问题讨论】:

所以如果没有箱号/十字路口,括号也会消失吗?换句话说,记录可能看起来像(UNIT1) 911-STRU aBusiness 12345 Street aTown building fire, persons reported #F123456,在这种情况下,后续问题是:如何确定消息的一部分在哪里结束,下一部分从哪里开始? 是的蒂姆,这正是问题所在。我使用正则表达式取得了一些有限的成功。我可以让它与一条消息完美配合,但是当您添加格式略有不同的新消息时,其中一半会分崩离析。 【参考方案1】:

我认为你最好/最简单的选择是使用regular expressions,定义一个模式来匹配你的输入字符串的全部或部分并提取你感兴趣的部分。

PyParsing 可能也可以正常工作。我自己没有使用过它,但前几个示例看起来像是某种围绕正则表达式的更高级别的包装器,尽管我预计一旦您深入研究它,它会在许多方面有所不同。

另一个选择是定义一个lexer 并使用PLY 从它创建一个解析器。但是,这对于您的用例来说可能是多余的,因为它更多地旨在解析编程语言和自然语言语法。

【讨论】:

Simon 我可能爱你也恨你很多年,因为你向我介绍了正则表达式。我简直是疯了。它的用途有很多可能性。我遇到了一些问题,但是消息格式略有不同。我得再修补一些。 哈哈,我很高兴听到这个消息!我敢肯定,你会被标志性的引用 Some people, when confronted with a problem, think "I know, I'll use regular expressions." Now they have two problems. 逗乐。 :)【参考方案2】:

如果您了解 pyparsing,那么使用它可能会更容易。 () 总是可以被视为可选的。 Pyparsing 将使某些事情更容易开箱即用。

如果您对 pyparsing 不是很熟悉,并且您的主要目标是学习 Python,那么您可以在纯 Python 中手工制作自己的解析器。没有什么比重新发明一些***更适合学习一门新语言了:-)

【讨论】:

以上是关于Python regex - 字符串中的可选字段的主要内容,如果未能解决你的问题,请参考以下文章

表单输入文本模式中的可选字符

为啥我的 Diesel 结构中的可选字段没有实现特征

在 Python 中匹配 Unicode 字边界

django 模型中的可选字段

proto 3 中的可选(重复)字段

SwiftUI 文本字段中的可选链接绑定值