Как правильно разбить этот список строк?

У меня есть список строк, таких как:

['z+2-44', '4+55+z+88']

Как я могу разбить эти строки в списке так, чтобы это было что-то вроде

[['z','+','2','-','44'],['4','+','55','+','z','+','88']]

Я уже пытался использовать метод split, который уже разделяет 44 на 4 и 4, и я не уверен, что еще попробовать.

Ответы

Ответ 1

Вы можете использовать регулярное выражение:

import re
lst = ['z+2-44', '4+55+z+88']
[re.findall('\w+|\W+', s) for s in lst]
# [['z', '+', '2', '-', '44'], ['4', '+', '55', '+', 'z', '+', '88']]

\w+|\W+ соответствует шаблону, состоящему либо из символов слова (буквенно-цифровые значения в вашем случае), либо несловных символов (+- знаков в вашем случае).

Ответ 2

Это будет работать, используя itertools.groupby

z = ['z+2-44', '4+55+z+88']

print([["".join(x) for k,x in itertools.groupby(i,str.isalnum)] for i in z])

выход:

[['z', '+', '2', '-', '44'], ['4', '+', '55', '+', 'z', '+', '88']]

Он просто группирует символы, если они являются буквенно-цифровыми (или нет), просто присоединяйте их обратно в понимании списка.

EDIT: общий случай калькулятора с круглыми скобками задан в качестве последующего вопроса здесь. Если z выглядит следующим образом:

z = ['z+2-44', '4+55+((z+88))']

то с предыдущей группировкой получим:

[['z', '+', '2', '-', '44'], ['4', '+', '55', '+((', 'z', '+', '88', '))']]

Это непросто разобрать в терминах токенов. Таким образом, изменение будет join только в том случае, если alphanum и пусть в качестве списка, если нет, сглаживание в конце с помощью chain.from_iterable:

print([list(itertools.chain.from_iterable(["".join(x)] if k else x for k,x in itertools.groupby(i,str.isalnum))) for i in z])

который дает:

[['z', '+', '2', '-', '44'], ['4', '+', '55', '+', '(', '(', 'z', '+', '88', ')', ')']]

(обратите внимание, что альтернативный ответ регулярного выражения также можно адаптировать следующим образом: [re.findall('\w+|\W', s) for s in lst] (обратите внимание на отсутствие + после W)

также "".join(list(x)) немного быстрее, чем "".join(x), но я позволю вам добавить его, чтобы избежать изменения видимости этого уже сложного выражения.

Ответ 3

Альтернативное решение с использованием re.split:

l = ['z+2-44', '4+55+z+88']
print([list(filter(None, re.split(r'(\w+)', i))) for i in l])

Выход:

[['z', '+', '2', '-', '44'], ['4', '+', '55', '+', 'z', '+', '88']]

Ответ 4

Вы можете использовать только встроенные функции str.replace() и str.split() в понимании списка:

In [34]: lst = ['z+2-44', '4+55+z+88']

In [35]: [s.replace('+', ' + ').replace('-', ' - ').split() for s in lst]
Out[35]: [['z', '+', '2', '-', '44'], ['4', '+', '55', '+', 'z', '+', '88']]

Но обратите внимание, что это не эффективный подход для более длинных строк. В этом случае лучше всего использовать регулярное выражение.

В качестве еще одного питонического способа вы также можете использовать модуль tokenize:

In [56]: from io import StringIO

In [57]: import tokenize

In [59]: [[t.string for t in tokenize.generate_tokens(StringIO(i).readline)][:-1] for i in lst]
Out[59]: [['z', '+', '2', '-', '44'], ['4', '+', '55', '+', 'z', '+', '88']]

Модуль tokenize предоставляет лексический сканер для исходного кода Python, реализованный в Python. Сканер в этом модуле также возвращает комментарии в качестве токенов, что делает его полезным для реализации "симпатичных принтеров", в том числе колоризаторов для экранных дисплеев.

Ответ 5

Если вы хотите придерживаться split (следовательно, избегая регулярного выражения), вы можете предоставить ему необязательный символ для разделения:

>>> testing = 'z+2-44'
>>> testing.split('+')
['z', '2-44']
>>> testing.split('-')
['z+2', '44']

Итак, вы можете взломать что-то, связав команды разделения.

Однако использование регулярных выражений, вероятно, было бы более читаемым:

import re

>>> re.split('\+|\-', testing)
['z', '2', '44']

Это просто означает "разделить строку на любой символ + или" (обратные косые черты являются escape-символами, потому что оба они имеют особое значение в регулярном выражении.

Наконец, в этом конкретном случае, я полагаю, что цель - это что-то вроде "раскола при каждом неабелевом числовом символе", и в этом случае регулярное выражение еще может сохранить день:

>>> re.split('[^a-zA-Z0-9]', testing)
['z', '2', '44']

Конечно, стоит отметить, что существует еще миллион других решений, как обсуждалось в некоторых других обсуждениях SO.

Python: Разделить строку с несколькими разделителями

Разделить строки с несколькими разделителями?

Мои ответы здесь нацелены на простой, читаемый код, а не на производительность, в честь Дональда Кнута