对于初学者来说,这不是一件容易的事。你有几个步骤:
- 将文本拆分为单词
- 找到单位和相关值
- 将源单位值转换为目标单位值
- 替换单词中的单位值并重新组合文本。
这是一个概念证明(为了简洁起见,我使用了很多列表推导,但您应该将它们扩展为函数)。让我们试试你的例子:
>>> sentence = "The computer had 8 GB of RAM, and was able to complete the operation in 120 sec."
将文本拆分成单词
首先,您需要将句子解析为块。使用正则表达式。模式\W 匹配所有不是单词的东西。使用括号,您可以保留分隔符:
>>> import re
>>> chunks = re.split("(\W)", sentence)
>>> chunks
['The', ' ', 'computer', ' ', 'had', ' ', .... , '120', ' ', 'sec', '.', '']
重新组合文本很容易:
>>> "".join(chunks)
'The computer had 8 GB of RAM, and was able to complete the operation in 120 sec.'
找到单位和相关值
您对空间不感兴趣。使用enumerate 获取索引并删除空格(c.strip() 是False):
>>> enumerated_chunks = [(i, c) for i, c in enumerate(chunks) if c.strip()]
>>> enumerated_chunks
[(0, 'The'), (2, 'computer'), (4, 'had'), (6, '8'), (8, 'GB'), (10, 'of'), (12, 'RAM'), (13, ','), (16, 'and'), (18, 'was'), (20, 'able'), (22, 'to'), (24, 'complete'), (26, 'the'), (28, 'operation'), (30, 'in'), (32, '120'), (34, 'sec'), (35, '.')]
假设您有两个单位族:大小和时间。
>>> source_units = ('gb', 'sec') # ordered : sizes then times
如果我们 zip 和 enumerated_chunks 列表本身,我们有几个连续的块:
>>> list(zip(enumerated_chunks, enumerated_chunks[1:]))
[((0, 'The'), (2, 'computer')), ((2, 'computer'), (4, 'had')), ..., ((34, 'sec'), (35, '.'))]
假设你总是有单位后面的值(这是一个强假设),你将在句子中查找单位,并存储单位前面的值:
>>> value_units = []
>>> for (i, v), (j, u) in zip(enumerated_chunks, enumerated_chunks[1:]):
... u = u.casefold() # compare lowercase strings
... if u in source_units and v.isdigit(): # a unit predeeded by an integer
... value_units.append(((i, float(v)), (j, u), source_units.index(u))) # store the index of the family
>>> value_units
[((6, 8.0), (8, 'gb'), 0), ((32, 120.0), (34, 'sec'), 1)]
将源单位值转换为目标单位值
单位之间的转换表是一个矩阵,但我们可以想象我们有一个值为 1 的参考单位,其他只是倍数:
>>> sizes = {'gb':10**9, 'mb':10**6, 'kb':10**3, 'b':1}
>>> times = {'hour':3600, 'min':60, 'sec':1}
>>> families = (sizes, times) # same order as source_units
让目标单位:
>>> target_units = ('mb', 'min') # ordered as families
我们得到目标单位并想找到家庭:
>>> value_units_and_more = [((i, v), (j, u), target_units[f], families[f]) for (i, v), (j, u), f in value_units]
>>> value_units_and_more # you should use classes instead of tuples of tuples
[((6, 8.0), (8, 'gb'), 'mb', {'gb': 1000000000, 'mb': 1000000, 'kb': 1000, 'b': 1}), ((32, 120.0), (34, 'sec'), 'min', {'hour': 3600, 'min': 60, 'sec': 1})]
>>> indexed_chunks = [((i, v*vals[su]/vals[tu]), (j, tu)) for (i, v), (j, su), tu, vals in value_units_and_more]
>>> indexed_chunks
[((6, 8000.0), (8, 'mb')), ((32, 2.0), (34, 'min'))]
替换单词中的单位值并重新组合文本
我们将元组展平并创建一个字典index -> new word:
>>> new_chunk_by_index = dict(t for ts in indexed_chunks for t in ts)
>>> new_chunk_by_index
{6: 8000.0, 8: 'mb', 32: 2.0, 34: 'min'}
并替换单词:
>>> "".join(str(new_chunk_by_index.get(i, chunk)) for i, chunk in enumerate(chunks))
'The computer had 8000.0 mb of RAM, and was able to complete the operation in 2.0 min.'
(d.get(i, chunk) 取 dict new_chunk_by_index 中映射到 i 的值,如果没有值则离开 chunks。)
警告:这只是概念验证。肯定有很多边缘情况:想想复数、浮点值、单位在值之前的情况......
您还有另一个(也许更简单)的解决方案:使用re.sub 定位数字后跟一个单位,repl 是一个执行上述转换的函数。