У меня есть строка, содержащая имена и значения переменных. Между именами и значениями нет обозначенного разделителя, а имена могут содержать или не содержать подчеркивания.
string1 = 'Height_A_B132width_top100.0lengthsimple0.00001'
Я хотел бы получить переменные в словарь:
# desired output: dict1 = {'Height_A_B': 132, 'width_top': 100.0, 'lengthsimple': 0.00001}
Попытка следующего метода itertools
Input1 :
from itertools import groupby
[''.join(g) for _, g in groupby(string1, str.isdigit)]
Output1 :
['Height_A_B', '132', 'width_top', '100', '.', '0', 'lengthsimple', '0', '.', '00001']
Следующее должно почти получиться, но интерпретатор iPython говорит мне, что этот атрибут str не существует (он есть в документации). Тем не мение...
Input2 :
[''.join(g) for _, g in groupby(string1, str.isnumeric)]
Выход2 :
---------------------------------------------------------------------------
AttributeError Traceback (most recent call last)
<ipython-input-25-cf931a137f50> in <module>()
----> 1 [''.join(g) for _, g in groupby(string1, str.isnumeric)]
AttributeError: type object 'str' has no attribute 'isnumeric'
В любом случае, что произойдет, если число содержит показатель степени с символом «+» или «-»?
string2 = 'Height_A132width_top100.0lengthsimple1.34e+003'
# desired output: dict2 = {'Height_A_B': 132, 'width_top': 100.0, 'lengthsimple': 1.34e+003}
Данные 3 :
[''.join(g) for _, g in groupby(string2, str.isdigit)]
Output3 :
['Height_A', '132', 'width_top', '100', '.', '0', 'lengthsimple', '1', '.', '34', 'e+', '003']
Интересно, есть ли у кого-нибудь элегантное решение?
< Сильный > UPDATE : Ниже обсуждается вопрос о сохранении типов числовых переменных (например, int, float и т. Д.). На самом деле научная запись в string2 оказалась чем-то вроде красной сельди, потому что если вы создадите переменную
>>> a = 1.34e+003
Вы получаете
>>> print a
1340.0
В любом случае, вероятность получения струны с 1,34 + 003 в ней невелика.
Таким образом, string2 является более подходящим тестовым примером, если мы изменим его, скажем,
string2 = 'Height_A132width_top100.0lengthsimple1.34e+99'
4 ответа
Вы можете использовать регулярное выражение: ([^\d.]+)(\d[\d.e+-]*)
:
[^\d.]
означает: все, кроме цифр и точки+
означает один или несколько.- другой группе нужна хотя бы одна цифра, затем номер или e или - / +.
Группа 1 - это ключ, группа 2 - это значение.
Код:
import re
vals = { x:float(y) if '.' in y else int(y) for (x,y) in (re.findall(r'([^\d.]+)(\d[\d.e+-]*)',string2))}
{'width_top': 100.0, 'Height_A': 132, 'lengthsimple': 1340.0}
Обработка чисел в научной записи делает это немного сложным, но это возможно с тщательно написанным регулярным выражением. Надеюсь, мое регулярное выражение ведет себя правильно на всех данных. :)
import re
def parse_numstr(s):
''' Convert a numeric string to a number.
Return an integer if the string is a valid representation of an integer,
Otherwise return a float, if its's a valid rep of a float,
Otherwise, return the original string '''
try:
return int(s)
except ValueError:
try:
return float(s)
except ValueError:
return s
pat = re.compile(r'([A-Z_]+)([-+]?[0-9.]+(?:e[-+]?[0-9]+)?)', re.I)
def extract(s):
return dict((k, parse_numstr(v)) for k,v in pat.findall(s))
data = [
'Height_A_B132width_top100.0lengthsimple0.00001',
'Height_A132width_top100lengthsimple1.34e+003',
'test_c4.2E1p-3q+5z123E-2e2.71828',
]
for s in data:
print(extract(s))
вывод
{'Height_A_B': 132, 'width_top': 100.0, 'lengthsimple': 1.0000000000000001e-05}
{'width_top': 100, 'Height_A': 132, 'lengthsimple': 1340.0}
{'q': 5, 'p': -3, 'z': 1.23, 'test_c': 42.0, 'e': 2.71828}
Обратите внимание, что мое регулярное выражение будет принимать искаженные числа в научной нотации, которые содержат несколько десятичных точек, которые parse_numstr
будут просто возвращать в виде строк. Это не должно быть проблемой, если ваши данные не содержат таких искаженных чисел.
Вот немного лучшее регулярное выражение. Он допускает только одну десятичную точку, но также будет принимать искаженные числа без цифр по обе стороны от десятичной точки, например .
или .E1
и т. Д.
pat = re.compile(r'([A-Z_]+)([-+]?[0-9]*\.?[0-9]*(?:e[-+]?[0-9]+)?)', re.I)
См. Также этот ответ для регулярного выражения, которое фиксирует числа в научной нотации.
Это простое регулярное выражение будет работать:
[0-9.+e]+|\D+
Для создания ваших воздуховодов:
def pairs(s):
mtch = re.finditer("[0-9.+e]+|\D+", s)
m1, m2 = next(mtch, ""), next(mtch, "")
while m1:
yield m1.group(), float(m2.group())
m1, m2 = next(mtch, ""), next(mtch, "")
Демо:
In [27]: s = 'Height_A_B132width_top100.0lengthsimple0.00001'
In [28]: print(dict(pairs(s)))
{'Height_A_B': 132.0, 'width_top': 100.0, 'lengthsimple': 1e-05}
In [29]: s = 'Height_A132width_top100.0lengthsimple1.34e+003'
In [30]: print(dict(pairs(s)))
{'width_top': 100.0, 'Height_A': 132.0, 'lengthsimple': 1340.0}
Или для более общего подхода вы можете использовать ast.literal_eval
для анализа значений для работы с несколькими типами:
from ast import literal_eval
def pairs(s):
mtch = re.finditer("[0-9.+e]+|\D+", s)
m1, m2 = next(mtch, ""), next(mtch, "")
while m1:
yield m1.group(), literal_eval(m2.group())
m1, m2 = next(mtch, ""), next(mtch, "")
Что, если вы действительно беспокоитесь о ints vs float:
In [31]: s = 'Height_A132width_top100.0lengthsimple1.34e+99'
In [32]: dict(pairs(s))
Out[32]: {'Height_A': 132, 'lengthsimple': 1.34e+99, 'width_top': 100.0}
Ну вот:
import re
p = re.compile(ur'([a-zA-z]+)([0-9.]+)')
test_str = u"Height_A_B132width_top100.0lengthsimple0.00001"
print dict(re.findall(p, test_str))
Похожие вопросы
Связанные вопросы
Новые вопросы
python
Python - это многопарадигмальный, динамически типизированный, многоцелевой язык программирования. Он разработан для быстрого изучения, понимания и использования, а также для обеспечения чистого и единообразного синтаксиса. Обратите внимание, что Python 2 официально не поддерживается с 01.01.2020. Тем не менее, для вопросов о Python, связанных с версией, добавьте тег [python-2.7] или [python-3.x]. При использовании варианта Python (например, Jython, PyPy) или библиотеки (например, Pandas и NumPy) включите его в теги.