我有包含数字及其单位的字符串,例如2GB、17 英尺等 我想将数字与单位分开并创建 2 个不同的字符串。有时,它们之间有空格(例如 2 GB),使用 split(' ') 很容易做到这一点。
当它们在一起时(例如 2GB),我会测试每个字符,直到找到字母而不是数字。
s='17GB'
number=''
unit=''
for c in s:
if c.isdigit():
number+=c
else:
unit+=c
有更好的方法吗?
谢谢
找到第一个非数字字符即可跳出循环
for i,c in enumerate(s):
if not c.isdigit():
break
number = s[:i]
unit = s[i:].lstrip()
如果有负数和小数:
numeric = '0123456789-.'
for i,c in enumerate(s):
if c not in numeric:
break
number = s[:i]
unit = s[i:].lstrip()
您可以使用正则表达式将字符串分组:
>>> import re
>>> p = re.compile('(\d+)\s*(\w+)')
>>> p.match('2GB').groups()
('2', 'GB')
>>> p.match('17 ft').groups()
('17', 'ft')
tokenize
可以帮助:
>>> import StringIO
>>> s = StringIO.StringIO('27GB')
>>> for token in tokenize.generate_tokens(s.readline):
... print token
...
(2, '27', (1, 0), (1, 2), '27GB')
(1, 'GB', (1, 2), (1, 4), '27GB')
(0, '', (2, 0), (2, 0), '')
s='17GB'
for i,c in enumerate(s):
if not c.isdigit():
break
number=int(s[:i])
unit=s[i:]
您应该使用正则表达式,将您想要查找的内容分组在一起:
import re
s = "17GB"
match = re.match(r"^([1-9][0-9]*)\s*(GB|MB|KB|B)$", s)
if match:
print "Number: %d, unit: %s" % (int(match.group(1)), match.group(2))
根据您要解析的内容更改正则表达式。如果您不熟悉正则表达式,这里是一个很棒的教程网站。
>>> s="17GB"
>>> ind=map(str.isalpha,s).index(True)
>>> num,suffix=s[:ind],s[ind:]
>>> print num+":"+suffix
17:GB
这使用了一种比正则表达式更宽容的方法。注意:这不如发布的其他解决方案那么高效。
def split_units(value):
"""
>>> split_units("2GB")
(2.0, 'GB')
>>> split_units("17 ft")
(17.0, 'ft')
>>> split_units(" 3.4e-27 frobnitzem ")
(3.4e-27, 'frobnitzem')
>>> split_units("9001")
(9001.0, '')
>>> split_units("spam sandwhiches")
(0, 'spam sandwhiches')
>>> split_units("")
(0, '')
"""
units = ""
number = 0
while value:
try:
number = float(value)
break
except ValueError:
units = value[-1:] + units
value = value[:-1]
return number, units.strip()
这种解析器已经集成到Pint中:
Pint 是一个 Python 包,用于定义、操作和操纵物理 数量:数值和单位的乘积 测量。它允许它们之间的算术运算 不同单位之间的转换。
您可以使用
pip install pint
安装它。
然后,您可以解析一个字符串,获取所需的值('magnitude')及其单位:
>>> from pint import UnitRegistry
>>> ureg = UnitRegistry()
>>> size = ureg('2GB')
>>> size.m
2
>>> size.u
<Unit('gigabyte')>
>>> size.to('GiB')
<Quantity(1.86264515, 'gibibyte')>
>>> length = ureg('17ft')
>>> length.m
17
>>> length.u
<Unit('foot')>
>>> length.to('cm')
<Quantity(518.16, 'centimeter')>
尝试下面的正则表达式模式。第一组(以任何方式表示数字的 scanf() 标记)直接从 re 模块的 python 文档中提取。
import re
SCANF_MEASUREMENT = re.compile(
r'''( # group match like scanf() token %e, %E, %f, %g
[-+]? # +/- or nothing for positive
(\d+(\.\d*)?|\.\d+) # match numbers: 1, 1., 1.1, .1
([eE][-+]?\d+)? # scientific notation: e(+/-)2 (*10^2)
)
(\s*) # separator: white space or nothing
( # unit of measure: like GB. also works for no units
\S*)''', re.VERBOSE)
'''
:var SCANF_MEASUREMENT:
regular expression object that will match a measurement
**measurement** is the value of a quantity of something. most complicated example::
-666.6e-100 units
'''
def parse_measurement(value_sep_units):
measurement = re.match(SCANF_MEASUREMENT, value_sep_units)
try:
value = float(measurement[1])
except ValueError:
print("doesn't start with a number", value_sep_units)
units = measurement[6]
return(value, units)
对于这个任务,我肯定会使用正则表达式:
import re
there = re.compile(r'\s*(\d+)\s*(\S+)')
thematch = there.match(s)
if thematch:
number, unit = thematch.groups()
else:
raise ValueError('String %r not in the expected format' % s)
在 RE 模式中,
\s
表示“空白”,\d
表示“数字”,\S
表示非空白; *
表示“前面的 0 个或多个”,+
表示“前面的 1 个或多个”,括号括起“捕获组”,然后由对匹配对象的 groups()
调用返回。(如果给定字符串与模式不对应,则 thematch
为 None:可选空格,然后是一个或多个数字,然后是可选空格,然后是一个或多个非空格字符)。
正则表达式。
import re
m = re.match(r'\s*(?P<n>[-+]?[.0-9])\s*(?P<u>.*)', s)
if m is None:
raise ValueError("not a number with units")
number = m.group("n")
unit = m.group("u")
这将为您提供一个带有可选符号的数字(整数或定点;很难区分科学记数法的“e”与单位前缀),后面是单位,带有可选的空格。
如果您要进行大量比赛,可以使用
re.compile()
。
科学记数法 这个正则表达式对我来说可以很好地解析可能采用科学记数法的数字,并且基于最近关于 scanf 的 python 文档: https://docs.python.org/3/library/re.html#simulated-scanf
units_pattern = re.compile("([-+]?(\d+(\.\d*)?|\.\d+)([eE][-+]?\d+)?|\s*[a-zA-Z]+\s*$)")
number_with_units = list(match.group(0) for match in units_pattern.finditer("+2.0e-1 mm"))
print(number_with_units)
>>>['+2.0e-1', ' mm']
n, u = number_with_units
print(float(n), u.strip())
>>>0.2 mm
不幸的是,之前的代码在我的情况下都无法正常工作。我开发了以下代码。代码背后的想法是每个数字都以数字或点结尾。
def splitValUnit(s):
s = s.replace(' ', '')
lastIndex = len(s) - 1
i = lastIndex
for i in range(lastIndex, -1, -1):
if (s[i].isdigit() or s[i] == '.'):
break
i = i + 1
value = 0
unit = ''
try:
value = float(s[:i])
unit = s[i:]
except:
pass
return {'value': value, 'unit': unit}
print(splitValUnit('7')) #{'value': 7.0, 'unit': ''}
print(splitValUnit('+7')) #{'value': 7.0, 'unit': ''}
print(splitValUnit('7m')) #{'value': 7.0, 'unit': 'm'}
print(splitValUnit('27')) #{'value': 27.0, 'unit': ''}
print(splitValUnit('7.')) #{'value': 7.0, 'unit': ''}
print(splitValUnit('2GHz')) #{'value': 2.0, 'unit': 'GHz'}
print(splitValUnit('+2.e-10H')) #{'value': 2e-10, 'unit': 'H'}
print(splitValUnit('2.3e+4 MegaOhm'))#{'value': 23000.0, 'unit': 'MegaOhm'}
print(splitValUnit('-4.')) #{'value': -4.0, 'unit': ''}
print(splitValUnit('e mm')) #{'value': 0, 'unit': ''}
print(splitValUnit('')) #{'value': 0, 'unit': ''}