以特定的顺序并以这些正则表达式为条件,通过多次调用re()方法来替换字符串


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位

millon=1位数字后接6位

billon=1位数字后接6+6位

trillon=1位数字后接6+6位数字

cuatrilon=1位数字后接6+6+6位数字

它们之间的差值是6,总是6位数字,如果它们不完整,则表示为0,因为十进制是位置性的(重要数字的位置(。

当它以单数形式说时,例如millon,这是因为前面总是有一个1,即"1 millon"而不是"1 millones"(加es表示非单数(,但如果它大于1,它将是例如"2 trillones" = 2000000000000000000"320 billones" = 320000000000000

"mil"是一个例外,因为它不具有复数,也就是说,不使用2000个"2 miles",而是放置"2 mil"

另一个例外是没有写入1000个"1 mil",但我只写入"mil",可以理解这是"1000"

xxx mil xxx的Proto-regex

r"d{3}[s|]*(?:mil)[s|]*d{3}"

millon、billon、trillon和cuatrilon 的Proto-regex

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(((获得的输出,该方法是放置一些regex,因为替换必须以在要完成的数量的中间为条件,否则就不应该完成(如示例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 and eso daría por resultado various millones or trilones de unidades

执行我的西班牙语:(

相关内容

最新更新