所以我在做一个解析器,发现一个问题。确实,要解析数字,我有:
from pyparsing import Word, nums
n = Word(nums)
这对于没有数千个分隔符的数字非常有效。例如,n.parseString("1000", parseAll=True)
返回(['1000'], {})
,因此可以正常工作。
但是,当我添加千位分隔符时,它不起作用。确实,n.parseString("1,000", parseAll=True)
引起pyparsing.ParseException: Expected end of text, found ',' (at char 1), (line:1, col:2)
。
如何用千位分隔符解析数字?我不只是想忽略逗号(例如,n.parseString("1,00", parseAll=True)
应该返回错误,因为它不是数字)。
答案 0 :(得分:2)
首先处理字符串时,可以很好地在其上使用正则表达式以确保它确实是一个数字(包括数千个sep)。如果是这样,请替换每个逗号并将其输入解析器:
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
将产生Not a number
,请参见regex101.com上的表达式演示。
答案 1 :(得分:2)
一种纯粹的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所发布的代码,并将编译后的正则表达式传递给Regex构造函数。
要将解析时间转换为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"
作为故意错误包含在内,以演示runTests
输出的错误。)
int_with_thousands_separators.runTests("""\
1
# invalid value
1,00
1,000
-3,000,100
""")
如果要解析实数,请添加小数点以表示尾随的小数点和后续数字。
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
""")
答案 2 :(得分:0)
我不太理解“带数千个分隔符的数字”的意思。
无论如何,使用pyparsing都应该定义要解析的内容的模式。
在第一个示例中,pyparse很好地工作只是因为您将n定义为一个数字,所以:
n = Word(nums)
print(n.parseString("1000", parseAll=True))
['1000']
因此,如果要解析“ 1,000”或“ 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']
答案 3 :(得分:0)
我还想出了一个正则表达式解决方案,有点晚了:
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"))