Подтвердить что ты не робот

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

Я получил строку такого формата:

"Wilbur Smith (Billy, son of John), Eddie Murphy (John), Elvis Presley, Jane Doe (Jane Doe)"

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

Моя цель - разбить эту строку на список пар - (actor name, actor role).

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

Я думал о сплите его с помощью регулярного выражения: сначала разделим строку на скобки:

import re
x = "Wilbur Smith (Billy, son of John), Eddie Murphy (John), Elvis Presley, Jane Doe (Jane Doe)"
s = re.split(r'[()]', x) 
# ['Wilbur Smith ', 'Billy, son of John', ', Eddie Murphy ', 'John', ', Elvis Presley, Jane Doe ', 'Jane Doe', '']

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

Есть ли какие-либо более простые/более приятные способы сделать это, либо с одним регулярным выражением, либо с хорошим кодом?

4b9b3361

Ответ 1

Один из способов сделать это - использовать findall с регулярным выражением, которое с жадностью соответствует вещам, которые могут проходить между разделителями. например:

>>> s = "Wilbur Smith (Billy, son of John), Eddie Murphy (John), Elvis Presley, Jane Doe (Jane Doe)"
>>> r = re.compile(r'(?:[^,(]|\([^)]*\))+')
>>> r.findall(s)
['Wilbur Smith (Billy, son of John)', ' Eddie Murphy (John)', ' Elvis Presley', ' Jane Doe (Jane Doe)']

Регулярное выражение выше соответствует одному или нескольким:

  • non-comma, символы без открытого пароля Строки
  • начинающиеся с открытого паре, содержат 0 или более не-закрытых парен, а затем близкий палец

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

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

"Wilbur Smith (son of John (Johnny, son of James), aka Billy), Eddie Murphy (John)"

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

Ответ 2

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

Поскольку только модуль csv позволяет один символ quotechar, вам нужно будет сделать замену на ваших входах для преобразования () на что-то вроде | или ". Затем убедитесь, что вы используете соответствующий диалект и отключаетесь.

Ответ 3

s = re.split(r',\s*(?=[^)]*(?:\(|$))', x) 

Смотрище сопоставляет все до следующей открытой или в конце строки, iff между ними нет закрывающей скобки. Это гарантирует, что запятая не находится внутри набора круглых скобок.

Ответ 4

Попытка читаемого пользователем регулярного выражения:

import re

regex = re.compile(r"""
    # name starts and ends on word boundary
    # no '(' or commas in the name
    (?P<name>\b[^(,]+\b)
    \s*
    # everything inside parentheses is a role
    (?:\(
      (?P<role>[^)]+)
    \))? # role is optional
    """, re.VERBOSE)

s = ("Wilbur Smith (Billy, son of John), Eddie Murphy (John), Elvis Presley,"
     "Jane Doe (Jane Doe)")
print re.findall(regex, s)

Вывод:

[('Wilbur Smith', 'Billy, son of John'), ('Eddie Murphy', 'John'), 
 ('Elvis Presley', ''), ('Jane Doe', 'Jane Doe')]

Ответ 5

Мой ответ не будет использовать регулярное выражение.

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

Моя попытка:

s = 'Wilbur Smith (Billy, son of John), Eddie Murphy (John), Elvis Presley, Jane Doe (Jane Doe)'

in_actor_name = 1
role = ''
name = ''
for c in s:
    if c == ')' or (c == ',' and in_actor_name):
        in_actor_name = 1
        name = name.strip()
        if name:
            print "%s: %s" % (name, role)
        name = ''
        role = ''
    elif c == '(':
        in_actor_name = 0
    else:
        if in_actor_name:
            name += c
        else:
            role += c
if name:
    print "%s: %s" % (name, role)

Вывод:

Wilbur Smith: Billy, son of John
Eddie Murphy: John
Elvis Presley: 
Jane Doe: Jane Doe

Ответ 6

Этот пост мне очень помог. Я искал разбиение строки запятыми, расположенными вне кавычек. Я использовал это как стартер. Моя последняя строка кода была regEx = re.compile(r'(?:[^,"]|"[^"]*")+') Это сделал трюк. Спасибо тонну.

Ответ 7

Я, конечно, согласен с @Wogan выше, что использование CSV moudle - хороший подход. Сказав, что если вы все еще хотите попробовать регулярное выражение, попробуйте, но вам придется адаптировать его к диалекту Python.

string.split(/,(?=(?:[^\"]*\"[^\"]*\")*(?![^\"]*\"))/)

НТН

Ответ 8

split by ")"

>>> s="Wilbur Smith (Billy, son of John), Eddie Murphy (John), Elvis Presley, Jane Doe (Jane Doe)"
>>> s.split(")")
['Wilbur Smith (Billy, son of John', ', Eddie Murphy (John', ', Elvis Presley, Jane Doe (Jane Doe', '']
>>> for i in s.split(")"):
...   print i.split("(")
...
['Wilbur Smith ', 'Billy, son of John']
[', Eddie Murphy ', 'John']
[', Elvis Presley, Jane Doe ', 'Jane Doe']
['']

вы можете продолжить проверку, чтобы получить те имена, которые не входят с().

Ответ 9

Вот общая техника, которую я использовал в прошлом для таких случаев:

Используйте функцию sub модуля re с функцией в качестве аргумента замены. Функция отслеживает открытие и закрытие парсеров, кронштейнов и брекетов, а также одиночные и двойные кавычки и выполняет замену только за пределами таких заключенных в скобки и подкатегорий. Затем вы можете заменить запятые без кавычек на другой символ, который, как вы уверены, не отображается в строке (я использую код ASCII/Unicode group-separator: chr (29)), а затем простую строку. раскол на этот символ. Здесь код:

import re
def srchrepl(srch, repl, string):
    """Replace non-bracketed/quoted occurrences of srch with repl in string"""

    resrchrepl = re.compile(r"""(?P<lbrkt>[([{])|(?P<quote>['"])|(?P<sep>["""
                            + srch + """])|(?P<rbrkt>[)\]}])""")
    return resrchrepl.sub(_subfact(repl), string)

def _subfact(repl):
    """Replacement function factory for regex sub method in srchrepl."""
    level = 0
    qtflags = 0
    def subf(mo):
        nonlocal level, qtflags
        sepfound = mo.group('sep')
        if  sepfound:
            if level == 0 and qtflags == 0:
                return repl
            else:
                return mo.group(0)
        elif mo.group('lbrkt'):
            level += 1
            return mo.group(0)
        elif mo.group('quote') == "'":
            qtflags ^= 1            # toggle bit 1
            return "'"
        elif mo.group('quote') == '"':
            qtflags ^= 2            # toggle bit 2
            return '"'
        elif mo.group('rbrkt'):
            level -= 1
            return mo.group(0)
    return subf

Если у вас нет nonlocal в вашей версии Python, просто измените его на global и определите level и qtflags на уровне модуля.

Вот как он используется:

>>> GRPSEP = chr(29)
>>> string = "Wilbur Smith (Billy, son of John), Eddie Murphy (John), Elvis Presley, Jane Doe (Jane Doe)"
>>> lst = srchrepl(',', GRPSEP, string).split(GRPSEP)
>>> lst
['Wilbur Smith (Billy, son of John)', ' Eddie Murphy (John)', ' Elvis Presley', ' Jane Doe (Jane Doe)']

Ответ 10

Ни один из приведенных выше ответов не является правильным, если в ваших данных есть какие-либо ошибки или шум.

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

Предположим, существуют скобки вложенности? Предположим, что есть несогласованные круглые скобки? Предположим, что строка заканчивается или начинается с запятой или имеет две строки?

Все вышеупомянутые решения будут производить больше или меньше мусора и не сообщать об этом вам.

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