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

#python #regex

#python #регулярное выражение

Вопрос:

У меня есть несколько строк в формате

 lorem ipsum, dolor sit - amet, consectetur : adipiscing elit. Praesent vitae orc
  

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

 ['lorem ipsum',
'dolor sit', 
'amet, consectetur', 
'adipiscing elit. Praesent vitae orc']
  

Прямо сейчас мой вывод

 ['lorem ipsum',
'dolor sit',
'amet',
'consectetur ',
'adipiscing elit. Praesent vitae orc']
  

Прямо сейчас я использую re.split(', | - |: ', txt) , но он разделяет все экземпляры в строке. Есть предложения о том, как я могу добиться требуемого результата?

Редактировать:

Я понял, что мой вопрос неясен, поэтому, например, если строка

 "abc: def: ijk, lmno: pqr - stu, wx"
  

вывод должен быть

 ["abc",
"def: ijk",
"lmno: pqr",
"stu, wxy"]
  

и не

 ["abc",
"def",
"ijk",
"lmno",
"pqr",
"stu",
"wxy"]
  

Комментарии:

1. Каков ваш желаемый результат?

2. @OrY желаемый результат находится в вопросе.

3. @MarkMeyer Запустите его регулярное выражение, это результат этого регулярного выражения. если это его желаемый результат, то у него нет проблем

4. @OrY виноват, добавил туда дополнительный пробел по ошибке, исправил это сейчас

5. @Thefourthbird да, это так

Ответ №1:

Если все разделители должны присутствовать хотя бы один раз, вместо использования разделения вы могли бы использовать 4 группы захвата с обратной ссылкой, соответствующей 1 из 3 вариантов, за исключением того, что уже сопоставлено.

 ^(.*?)(, | - |: )(.*?)(?!2)(, | - |: )(.*?)(?!2|4)(, | - |: )(.*)
  

Шаблон будет соответствовать

  • ^ Начало строки
  • (.*?) Группа 1, совпадающая как можно меньше
  • (, | - |: ) Группа 2, соответствует любому из перечисленных
  • (.*?) Группа 3, совпадение как можно меньше
  • (?!2) Отрицательный прогноз, утверждать, что то, что справа, не соответствует группе 2 (выберите один из 2 допустимых вариантов)
  • (, | - |: ) Группа 4, соответствует любому из перечисленных
  • (.*?) Группа 5, совпадение как можно меньше
  • (?!2|4) Отрицательный прогноз, утверждать, что то, что справа, не соответствует группе 2 или группе 4 (выберите единственный допустимый вариант слева)
  • (, | - |: ) Группа 6, соответствует любому из перечисленных
  • (.*) Группа 7, максимально сопоставьте любой символ

Демонстрация регулярных выражений

Например

 import re

regex = r"^(.*?)(, | - |: )(.*?)(?!2)(, | - |: )(.*?)(?!2|4)(, | - |: )(.*)"

test_str = ("lorem ipsum, dolor sit - amet , consectetur : adipiscing elit. Praesent vitae orcnn"
    "abc: def: ijk, lmno: pqr - stu, wxnn")

matches = re.search(regex, test_str, re.MULTILINE)

if matches:
    print(matches.group(1))
    print(matches.group(3))
    print(matches.group(5))
    print(matches.group(7))
  

Вывод

 lorem ipsum
dolor sit
amet , consectetur 
adipiscing elit. Praesent vitae orc
  

Смотрите Python demo1 и demo2

Комментарии:

1. Это было из-за плохого примера, который я выбрал, но скажем, что строка выглядит примерно так abc: def: ijk, lmno: pqr - stu, wxy , выходные данные должны быть разделены первыми экземплярами всех этих разделителей и должны возвращать ["abc", "def: ijk" "lmno: pqr", "stu, wxy"] Я отредактирую вопрос, чтобы отразить то же самое

2. @Eagle Итак, вы сначала хотите разделить на , Затем на ` — `, а затем на : ?

3. Больше похоже на разделение только в трех местах #1, где , появляется впервые # 2, где : появляется впервые # 3, где ` — ` появляется впервые

4. @Eagle Я изменил ответ, используя группы захвата.

Ответ №2:

Просто пища для размышлений, не уверен, что это ценный ответ, но, возможно, если вы сможете использовать модуль regex вместо re , чтобы использовать возможность отрицательного просмотра сзади с нефиксированной шириной. Например:

 s*([,:-])(?<!1.*1)s*
  

В Python:

 import regex as re
string1 = "abc: def: ijk, lmno: pqr - stu, wx"
lst1 = re.sub(r's*([,:-])(?<!1.*1)s*', '|' , string1).split('|')
print(lst1)
  

Результат:

 ['abc', 'def: ijk', 'lmno: pqr', 'stu, wx']
  

Комментарии:

1. Большое спасибо, это сработало, но как я мог разделить его на ` — ` вместо - и на : вместо : ? Например, с пробелами до и после - и пробелом после :

2. Не уверен, что я вас достаточно хорошо понимаю. Может быть, использовать вместо *?

Ответ №3:

Вы могли бы использовать небольшой класс, который подсчитывает замены:

 import re

text = "lorem ipsum, dolor sit - amet, consectetur : adipiscing elit. Praesent vitae orc"
# text = "abc: def: ijk, lmno: pqr - stu, wx"
rx = re.compile(r'[-,:]')


class Replacer:
    def __init__(self, *args, **kwargs):
        for key in args:
            setattr(self, key, 0)
        self.needle = kwargs.get("needle")

    def __call__(self, match):
        key = match.group(0)
        setattr(self, key, getattr(self, key, 0)   1)
        cnt = getattr(self, key, 0)
        return self.needle if cnt == 1 else key


rpl = Replacer("-", ",", ":", needle="#@#")

result = [item.strip() for item in re.split("#@#", rx.sub(rpl, text))]
print(result)
  

Что дает

 ['lorem ipsum', 'dolor sit', 'amet, consectetur', 'adipiscing elit. Praesent vitae orc']