import re
#Example 1
input_str = "creo que hay 330 trillones 2 billones 18 millones 320 mil 459 47475822"
#Example 2
input_str = "sumaria 6 cuatrillones 789 billones 320 mil a esta otra cantidad de elementos 47475822 y eso daría por resultado varios millones o trillones de unidades"
mil = 1000
2 mil = 2000
322 mil = 322000
1 millon = 1000000
2 millones = 2000000
1 billon = 1000000000000
25 billones = 25000000000000
1 trillon = 1000000000000000000
3 trillones = 3000000000000000000
1 cuatrillon = 1000000000000000000000000
mil = 1 位数字后跟 3 位数字
百万 = 1 位数字后跟 6 位数字
十亿 = 1 位数字后面是 6+6 位数字
万亿 = 1 位数字后跟 6+6+6 位数字
cuatrillon = 1 位数字后接 6+6+6+6 位数字
它们之间的差是6,总是6位,如果不完整,则表示为0,因为十进制是位置制(重要数字的位置)。
当用单数表示时,例如millon,是因为前面总有一个1,即
"1 millon"
而不是"1 millones"
(非单数加es)但如果大于1 ,例如 "2 trillones" = 2000000000000000000
或 "320 billones" = 320000000000000
。
"mil"
是一个例外,因为它没有复数,即不使用 2 千 "2 miles"
,而是放置 "2 mil"
。
另一个例外是1千
"1 mil"
没有写,但我只写了"mil"
,据了解是"1000"
xxx mil xxx
的原始正则表达式
r"\d{3}[\s|]*(?:mil)[\s|]*\d{3}"
millon、billon、trillon 和 cuatrillon 的原始正则表达式
r"\d{6}[\s|]*(?:cuatrillones|cuatrillon)[\s|]*\d{6}[\s|]*
(?:trillones|trillon)[\s|]*\d{6}[\s|]*(?:billones|billon)[\s|:]*\d{6}[\s|:]*(?:millones|millon)[\s|:]*\d{6}"
我需要使用像 re.sub() 这样的替换方法获得的输出,这个方法是放置一些正则表达式,因为替换必须条件限制在要完成的数字的中间,否则应该没有完成(如示例 2 的输出所示)
"3000000000000320459 47475822" #example 1
"sumaria 6000000000789000000320000 a esta otra cantidad de elementos 47475822 y eso daría por resultado varios millones o trillones de unidades" #example 2
如何改进我的正则表达式才能正确执行这些替换?或者也许使用其他方法更好?
双向:
import re
NUMBERS = [
(10**15, 'quatrillon', 'es', False),
(10**12, 'trillon', 'es', False),
(10**9, 'billon', 'es', False),
(10**6, 'millon', 'es', False),
(10**3, 'mil', '', True)
]
def num_to_name(n):
n = int(n) if isinstance(n, str) else n
for size, name, multi, alone in NUMBERS:
if n > size - 1:
n = n // size
if n == 1 and alone:
return f'{name}'
else:
return f'{n} {name}{multi if n > 1 else ""}'
return str(n)
def name_to_num(s, return_f=False):
s = s[:-2] if s.endswith('es') else s
for size, name, _, alone in NUMBERS:
if s.lower().endswith(name):
result = int(s[:-(len(name) + 1)]) * size if not alone or s.lower() != name else size
return (result, size) if return_f else result
return (int(s), 0) if return_f else int(s)
input_str = "creo que hay 330 trillones 2 billones 18 millones 320 mil 459 47475822 1000"
num_str = re.sub('\d+(?: (?:quatr|tr|b|m)illon(?:es)?| mil)?|mil',
lambda match: str(name_to_num(match.group(0))), input_str)
print(num_str)
name_str = re.sub('\d+',
lambda match: num_to_name(match.group(0)), num_str)
print(name_str)
输出:
creo que hay 330000000000000 2000000000 18000000 320000 459 47475822 1000
creo que hay 330 trillones 2 billones 18 millones 320 mil 459 47 millones mil
请注意,最终结果并不完全是输入字符串,因为输入字符串有一些可以转换的数字(如
'47 millones'
)。另外,您指出 1 mil
写为 mil
,因此向 NUMBERS
添加了一个附加字段来标记这一点,并调整 num_to_name()
来处理这种情况。
函数
num_to_name(n)
接受一个整数(或字符串,转换为整数),并使用 NUMBERS
中定义的命名找到将其写为数字的适当方法。如果它与任何尺寸都不匹配,它只会以字符串形式返回数字。
函数
name_to_num(s)
接受一个字符串并检查它是否以 NUMBERS
中定义的任何名称(带或不带复数)结尾。如果是,它会尝试将字符串的其余部分转换为整数,并返回该值乘以匹配因子。否则,它会尝试仅返回字符串的整数值。
在底部,有两个正则表达式匹配输入字符串的相关部分,使用 lambda 来替换使用 2 个函数找到的片段。
从您的评论中,我注意到您实际上希望将后续匹配的大小减小合并为一个单个数字 - 下面没有回答这个问题,我将保持代码不变)
此附加代码与第一部分一起执行此操作:
def full_name_to_num(s):
subs = []
last_f = 0
def sub(s):
s, end = (s[:-1], ' ') if s[-1] == ' ' else (s, '')
nonlocal last_f
n, f = name_to_num(s, True)
if subs and (f < last_f):
subs[-1] = subs[-1] + n
result = ''
else:
subs.append(n)
result = str(len(subs)-1) + end
last_f = f
return result
temp = re.sub('(?:\d+(?: (?:quatr|tr|b|m)illon(?:es)?| mil)?|mil) ?', lambda match: sub(match.group(0)), s)
return re.sub('\d+', lambda match: str(subs[int(match.group(0))]), temp)
def full_num_to_name(s):
def sub(s):
n = int(s)
result = [str(n % NUMBERS[-1][0])] if n % NUMBERS[-1][0] else []
for size, _, _, _ in reversed(NUMBERS):
if (n // size) % 1000:
result.append(num_to_name(n % (size * 1000)))
return ' '.join(reversed(result))
return re.sub('\d+', lambda match: sub(match.group(0)), s)
input_str = "creo que hay 330 trillones 2 billones 18 millones 320 mil 459 47475822"
full_num_str = full_name_to_num(input_str)
print(full_num_str)
full_name_str = full_num_to_name(full_num_str)
print(full_name_str)
额外输出:
creo que hay 330002018320459 47475822
creo que hay 330 trillones 2 billones 18 millones 320 mil 459 47 millones 475 mil 822
我认为你不应该使用纯正则表达式,而应该混合一些巧妙的算术解析。这是如何解决它的示例(请注意,它实际上以有意义的方式转换数字,而不仅仅是将它们连接起来,因此结果与您定义的期望有所不同)
import re
input_str1 = "creo que hay 330 trillones 2 billones 18 millones 320 mil 459 47475822"
input_str2 = "sumaria 6 cuatrillones 789 billones 320 mil a esta otra cantidad de elementos 47475822 y eso daría por resultado varios millones o trillones de unidades"
def wrap_word(word: str) -> str:
return fr"(\d+)\s+\b{word}\b"
def wrap_num(num: int) -> str:
return f"\\1*{str(num)}"
def eval_mult_exp(text: str) -> str:
for op1, op2 in re.findall("(\\d+)\*(\\d+)", text):
text = re.sub(pattern=op1+"\*"+op2, repl=str(int(op1)*int(op2)), string=text)
return text
def eval_addition_exp(text: str) -> str:
if not re.search("(\\d+) (\\d+)", text): # recursion halting condition
return text
for op1, op2 in re.findall("(\\d+) (\\d+)", text):
text = re.sub(pattern=op1+" "+op2, repl=str(int(op1)+int(op2)), string=text)
return eval_addition_exp(text)
def word_to_num(word: str) -> str:
for pattern, numeric_replacement in [
(wrap_word("mil"), wrap_num(10**3)),
(wrap_word("millones(es)?"), wrap_num(10**6)),
(wrap_word("billon(es)?"), wrap_num(10**9)),
(wrap_word("trillon(es)?"), wrap_num(10**12)),
(wrap_word("cuatrillon(es)?"), wrap_num(10**15)),
]:
word = re.sub(pattern, numeric_replacement, word)
return word
print(eval_addition_exp(eval_mult_exp(word_to_num(input_str2))))
输出[1]:
sumaria 6000789000320000 a esta otra cantidad de elementos 47475822 y eso daría por resultado varios millones o trillones de unidades
请原谅我的西班牙语:)