Python 正则表达式 - 字符串中的可选字段
Python regex - Optional fields in a string
我正在尝试自学 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\d{0,7})', sInput)
print pAppliances
print pJobSource
print pAddress
print pCrossStreet
print pJobDetails
print pJobNumber
当 运行 在未注释的 sInput 字符串上 return 如下
['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 行纠正了这个问题,但是当第一个字符串被应用时,它 return 根本没有地址,因为它没有找到括号。
所以新的重新聚焦的问题是:
如何在正则表达式行中添加可选参数。如果存在括号,请忽略它们及其内容,return 字符串的其余部分,或者如果没有括号,则继续按正常方式继续。
我认为你的 best/easiest 选项是使用 regular expressions,定义一个模式来匹配输入字符串的全部或部分并提取你感兴趣的片段。
PyParsing 也可能工作正常。我自己没有使用过它,但前几个示例看起来像是某种围绕正则表达式的更高级别的包装器,尽管我认为一旦您深入研究它,它在很多方面都会有所不同。
另一种选择是定义一个lexer and create a parser from it using PLY。但是,这对于您的用例来说可能有点矫枉过正,因为它更多地旨在解析编程语言和自然语言语法。
如果你知道 pyparsing,那么使用它可能会更容易。 ()
始终可以视为可选的。 Pyparsing 将使某些事情更容易开箱即用。
如果您对 pyparsing 不是很熟悉,并且您的主要目标是学习 python,那么您可以纯手工制作您自己的解析器 python。没有什么比重新发明一些轮子更能学习一门新语言了:-)
我正在尝试自学 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\d{0,7})', sInput)
print pAppliances
print pJobSource
print pAddress
print pCrossStreet
print pJobDetails
print pJobNumber
当 运行 在未注释的 sInput 字符串上 return 如下
['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 行纠正了这个问题,但是当第一个字符串被应用时,它 return 根本没有地址,因为它没有找到括号。
所以新的重新聚焦的问题是:
如何在正则表达式行中添加可选参数。如果存在括号,请忽略它们及其内容,return 字符串的其余部分,或者如果没有括号,则继续按正常方式继续。
我认为你的 best/easiest 选项是使用 regular expressions,定义一个模式来匹配输入字符串的全部或部分并提取你感兴趣的片段。
PyParsing 也可能工作正常。我自己没有使用过它,但前几个示例看起来像是某种围绕正则表达式的更高级别的包装器,尽管我认为一旦您深入研究它,它在很多方面都会有所不同。
另一种选择是定义一个lexer and create a parser from it using PLY。但是,这对于您的用例来说可能有点矫枉过正,因为它更多地旨在解析编程语言和自然语言语法。
如果你知道 pyparsing,那么使用它可能会更容易。 ()
始终可以视为可选的。 Pyparsing 将使某些事情更容易开箱即用。
如果您对 pyparsing 不是很熟悉,并且您的主要目标是学习 python,那么您可以纯手工制作您自己的解析器 python。没有什么比重新发明一些轮子更能学习一门新语言了:-)