Эффективно разбить строку на несколько разделителей и сохранить каждый разделитель?

Мне нужно разделить строки данных, используя каждый символ из string.punctuation и string.whitespace как разделитель.

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

Например,

 "Now is the winter of our discontent" 

должен выводить:

 ['Now', ' ', 'is', ' ', 'the', ' ', 'winter', ' ', 'of', ' ', 'our', ' ', 'discontent'] 

Я не уверен, как это сделать, не прибегая к оргии вложенных петель, что неприемлемо медленно. Как мне это сделать?

  • Поиск индексов в списках Python эффективно (по сравнению с MATLAB)
  • Как я могу использовать numpy без его установки?
  • ipynb импортирует другой файл ipynb
  • Отсутствует файл при установке pylinkgrammar
  • Python regex look-behind требует шаблона с фиксированной шириной
  • Где в исходном коде python определяется math.exp ()?
  • Матрица с изображением на tkinter Canvas
  • Python: разделение списка на под-списки на основе диапазонов индексов
  • 9 Solutions collect form web for “Эффективно разбить строку на несколько разделителей и сохранить каждый разделитель?”

    Другой подход, не относящийся к регулярному выражению от других:

     >>> import string >>> from itertools import groupby >>> >>> special = set(string.punctuation + string.whitespace) >>> s = "One two three tab\ttabandspace\t end" >>> >>> split_combined = [''.join(g) for k, g in groupby(s, lambda c: c in special)] >>> split_combined ['One', ' ', 'two', ' ', 'three', ' ', 'tab', '\t', 'tabandspace', '\t ', 'end'] >>> split_separated = [''.join(g) for k, g in groupby(s, lambda c: c if c in special else False)] >>> split_separated ['One', ' ', 'two', ' ', 'three', ' ', 'tab', '\t', 'tabandspace', '\t', ' ', 'end'] 

    Могу использовать dict.fromkeys и .get вместо lambda , я думаю.

    [редактировать]

    Некоторое объяснение:

    groupby принимает два аргумента, итеративную и (необязательную) ключевую функцию. Он перемещается по итерируемому и группирует их со значением ключевой функции:

     >>> groupby("sentence", lambda c: c in 'nt') <itertools.groupby object at 0x9805af4> >>> [(k, list(g)) for k,g in groupby("sentence", lambda c: c in 'nt')] [(False, ['s', 'e']), (True, ['n', 't']), (False, ['e']), (True, ['n']), (False, ['c', 'e'])] 

    где слагаемые со смежными значениями ключевой функции группируются вместе. (Это обычный источник ошибок, на самом деле – люди забывают, что сначала они должны сортировать по keyfunc, если они хотят группировать термины, которые могут быть не последовательными.)

    Как предположил @JonClements, я имел в виду

     >>> special = dict.fromkeys(string.punctuation + string.whitespace, True) >>> s = "One two three tab\ttabandspace\t end" >>> [''.join(g) for k,g in groupby(s, special.get)] ['One', ' ', 'two', ' ', 'three', ' ', 'tab', '\t', 'tabandspace', '\t ', 'end'] 

    для случая, когда мы объединяли сепараторы. .get возвращает None если значение не указано в dict.

     import re import string p = re.compile("[^{0}]+|[{0}]+".format(re.escape( string.punctuation + string.whitespace))) print p.findall("Now is the winter of our discontent") 

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

    Я объясню regexp, так как вы не знакомы с ним:

    • [...] означает любой из символов внутри квадратных скобок
    • [^...] означает любой из символов, не входящих в квадратные скобки
    • + позади означает одну или несколько предыдущих вещей
    • x|y означает совпадение либо x либо y

    Таким образом, регулярное выражение соответствует одному или нескольким символам, где либо все должно быть пунктуацией, либо пробелом, либо не должно быть. Метод findall находит все совпадающие совпадения шаблона.

    Попробуй это:

     import re re.split('(['+re.escape(string.punctuation + string.whitespace)+']+)',"Now is the winter of our discontent") 

    Объяснение из документации Python :

    Если в шаблоне используются скобки, то текст всех групп в шаблоне также возвращается как часть результирующего списка.

    Решение в линейном ( O(n) ) времени:

    Допустим, у вас есть строка:

     original = "a, b...cd" 

    Сначала преобразуйте все разделители в пространство:

     splitters = string.punctuation + string.whitespace trans = string.maketrans(splitters, ' ' * len(splitters)) s = original.translate(trans) 

    Теперь s == 'abc d' . Теперь вы можете использовать itertools.groupby для itertools.groupby между пробелами и не-пробелами:

     result = [] position = 0 for _, letters in itertools.groupby(s, lambda c: c == ' '): letter_count = len(list(letters)) result.append(original[position:position + letter_count]) position += letter_count 

    Теперь result == ['a', ', ', 'b', '...', 'c', ' ', 'd'] , что вам и нужно.

    Мой прием:

     from string import whitespace, punctuation import re pattern = re.escape(whitespace + punctuation) print re.split('([' + pattern + '])', 'now is the winter of') 

    В зависимости от текста, с которым вы имеете дело, вы можете упростить свою концепцию разделителей на «что угодно, кроме букв и цифр». Если это будет работать, вы можете использовать следующее регулярное выражение:

     re.findall(r'[a-zA-Z\d]+|[^a-zA-Z\d]', text) 

    Это предполагает, что вы хотите разделить на каждый отдельный символ разделителя, даже если они происходят последовательно, поэтому 'foo..bar' станет ['foo', '.', '.', 'bar'] . Если вместо этого вы ожидаете ['foo', '..', 'bar'] , используйте [a-zA-Z\d]+|[^a-zA-Z\d]+ (только разница добавляет + на самый конец).

     from string import punctuation, whitespace s = "..test. and stuff" f = lambda s, c: s + ' ' + c + ' ' if c in punctuation else s + c l = sum([reduce(f, word).split() for word in s.split()], []) print l 

    Для любого произвольного набора сепараторов:

     def separate(myStr, seps): answer = [] temp = [] for char in myStr: if char in seps: answer.append(''.join(temp)) answer.append(char) temp = [] else: temp.append(char) answer.append(''.join(temp)) return answer In [4]: print separate("Now is the winter of our discontent", set(' ')) ['Now', ' ', 'is', ' ', 'the', ' ', 'winter', ' ', 'of', ' ', 'our', ' ', 'discontent'] In [5]: print separate("Now, really - it is the winter of our discontent", set(' ,-')) ['Now', ',', '', ' ', 'really', ' ', '', '-', '', ' ', 'it', ' ', 'is', ' ', 'the', ' ', 'winter', ' ', 'of', ' ', 'our', ' ', 'discontent'] 

    Надеюсь это поможет

     from itertools import chain, cycle, izip s = "Now is the winter of our discontent" words = s.split() wordsWithWhitespace = list( chain.from_iterable( izip( words, cycle([" "]) ) ) ) # result : ['Now', ' ', 'is', ' ', 'the', ' ', 'winter', ' ', 'of', ' ', 'our', ' ', 'discontent', ' '] 
    Python - лучший язык программирования в мире.