Python pyparsing-使用千个分隔符分析数字
所以我在做一个解析器,我注意到了一个问题。事实上,要分析数字,我有:Python pyparsing-使用千个分隔符分析数字,python,python-3.x,pyparsing,Python,Python 3.x,Pyparsing,所以我在做一个解析器,我注意到了一个问题。事实上,要分析数字,我有: 从pyparsing导入单词,nums n=单词(nums) 这适用于没有数千个分隔符的数字。例如,n.parseString(“1000”,parseAll=True)返回(['1000'],{}),因此可以工作 但是,当我添加千分隔符时,它不起作用。实际上,n.parseString(“1000”,parseAll=True)引发了pyparsing.ParseException:预期的文本结尾,找到“,”(在字符1处)
从pyparsing导入单词,nums
n=单词(nums)
这适用于没有数千个分隔符的数字。例如,n.parseString(“1000”,parseAll=True)
返回(['1000'],{})
,因此可以工作
但是,当我添加千分隔符时,它不起作用。实际上,n.parseString(“1000”,parseAll=True)
引发了pyparsing.ParseException:预期的文本结尾,找到“,”(在字符1处),(第1行,第2列)
如何使用千个分隔符解析数字?我不想忽略逗号(例如,
n.parseString(“1,00”,parseAll=True)
应该返回一个错误,因为它不是一个数字)。当您首先处理字符串时,您可以很好地使用正则表达式来确保它确实是一个数字(包括)。如果是,则替换每个逗号并将其提供给解析器:
import re
from pyparsing import Word, nums
n = Word(nums)
def is_number(number):
rx = re.compile(r'^-?\d+(?:,\d{3})*$')
if rx.match(number):
return number.replace(",", "")
raise ValueError
try:
number = is_number("10,000,000")
print(n.parseString(number, parseAll=True))
except ValueError:
print("Not a number")
例如,
1,00
将导致不是一个数字
,请参阅上的表达式演示。我不太理解你所说的“带有数千个分隔符的数字”的意思
在任何情况下,使用pyparsing,您都应该定义要解析的模式
在第一个示例中,pyparse工作得很好,因为您将n定义为一个数字,所以:
n = Word(nums)
print(n.parseString("1000", parseAll=True))
['1000']
因此,如果要解析“1000”或“1,00”,则应将n定义为:
n = Word(nums) + ',' + Word(nums)
print(n.parseString("1,000", parseAll=True))
['1', ',', '000']
print(n.parseString("1,00", parseAll=True))
['1', ',', '00']
我还提出了一个正则表达式解决方案,有点晚了:
from pyparsing import Word, nums
import re
n = Word(nums)
def parseNumber(x):
parseable = re.sub('[,][0-9]{3}', lambda y: y.group()[1:], x)
return n.parseString(parseable, parseAll=True)
print(parseNumber("1,000,123"))
纯pyparsing方法将使用
Combine
包装一系列pyparsing表达式,这些表达式表示您在正则表达式中看到的不同字段:
import pyparsing as pp
int_with_thousands_separators = pp.Combine(pp.Optional("-")
+ pp.Word(pp.nums, max=3)
+ ("," + pp.Word(pp.nums, exact=3))[...])
我发现,构建这样的数值表达式会导致解析时间大大降低,因为所有这些独立的部分都是独立解析的,有多个内部函数和方法调用(这在Python中是真正的性能杀手)。因此,您可以使用Regex
将其替换为表达式:
# more efficient parsing with a Regex
int_with_thousands_separators = pp.Regex(r"-?\d{1,3}(,\d{3})*")
您还可以使用Jan发布的代码,并将编译后的正则表达式传递给正则表达式构造函数
要将解析时间转换为int,请添加一个去掉逗号的解析操作
# add parse action to convert to int, after stripping ','s
int_with_thousands_separators.addParseAction(
lambda t: int(t[0].replace(",", "")))
我喜欢使用runTests
检查这样的小表达式-编写一系列测试字符串很容易,输出显示解析结果或带有解析失败位置的带注释的输入字符串。(将“1,00”
作为故意错误包括在内,以演示运行测试的错误输出)
如果要分析实数,请添加表示尾随小数点和后面数字的片段
real_with_thousands_separators = pp.Combine(pp.Optional("-")
+ pp.Word(pp.nums, max=3)
+ ("," + pp.Word(pp.nums, exact=3))[...]
+ "." + pp.Word(pp.nums))
# more efficient parsing with a Regex
real_with_thousands_separators = pp.Regex(r"-?\d{1,3}(,\d{3})*\.\d+")
# add parse action to convert to float, after stripping ','s
real_with_thousands_separators.addParseAction(
lambda t: float(t[0].replace(",", "")))
real_with_thousands_separators.runTests("""\
# invalid values
1
1,00
1,000
-3,000,100
1.
# valid values
1.732
-273.15
""")
那不是我想要的。我不想解析
1,00
,因为它不是一个数字。此外,这只适用于仅带1000分隔符的数字,但不适用于例如1000000
,因为此数字有2000个分隔符separators@TheOneMusic:很高兴能帮上忙。事实上,我自己从来没有使用过pyparsing
,我最喜欢的是。如果你去掉“^”和“$”锚点(如果扫描一个较大表达式中的数字,这可能会混淆pyparsing),你可以用这个re来构建一个pyparsing正则表达式,然后把它合并成一个更大的表达式。@PaulMcG:我知道你是pyparsing
的真正作者。请随意编辑我的答案或自己提供一个答案,以包含您的解决方案。我不想从您那里窃取您的检查点-我始终感谢并鼓励其他人参与到问题中来。我写pyparsing是因为我对正则表达式的理解很糟糕,但就在此后的19年里,我实际上在正则表达式方面做得相当好。@PaulMcG:这不是因为名声,而是因为学习,不是吗。我甚至可以和-15 rep.@Jan一起睡个好觉。在这种情况下,这意味着要提出一个例外。这是一个无效的号码。阅读操作。传递给runTests
的一些示例字符串(例如“1,00”
,或不带小数部分的实数)是故意错误,以演示runTests
错误输出。我已经编辑以显示预期失败的示例字符串。
real_with_thousands_separators = pp.Combine(pp.Optional("-")
+ pp.Word(pp.nums, max=3)
+ ("," + pp.Word(pp.nums, exact=3))[...]
+ "." + pp.Word(pp.nums))
# more efficient parsing with a Regex
real_with_thousands_separators = pp.Regex(r"-?\d{1,3}(,\d{3})*\.\d+")
# add parse action to convert to float, after stripping ','s
real_with_thousands_separators.addParseAction(
lambda t: float(t[0].replace(",", "")))
real_with_thousands_separators.runTests("""\
# invalid values
1
1,00
1,000
-3,000,100
1.
# valid values
1.732
-273.15
""")