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

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

Текст выглядит следующим образом:

JT Meta Platforms, Inc. - Class A
Common Stock (META) [ST]S (partial) 02/08/2024 03/05/2024 $1,001 - $15,000
F S: New
S O: Morgan Stanley - Select UMA Account # 1
JT Microsoft Corporation - Common
Stock (MSFT) [ST]S (partial) 02/08/2024 03/05/2024 $1,001 - $15,000
F S: New
S O: Morgan Stanley - Select UMA Account # 1
JT Microsoft Corporation - Common
Stock (MSFT) [OP]P 02/13/2024 03/05/2024 $500,001 -
$1,000,000
F S: New
S O: Morgan Stanley - Portfolio Management Active Assets Account
D: Call options; Strike price $170; Expires 01/17 /2025
C: Ref: 044Q34N6

Я создал регулярное выражение для анализа отдельных транзакций, обозначаемых комбинацией тикера (например, (MSFT)), типа (например, [ST], [OP]) и суммы (например, 500 000 долларов США и т. д.) следующим образом:

transactions = rx.findall(r"\([A-Z][^$]*\$[^$]*\$[,\d]+", text)

Транзакции возвращаются в виде списка и выглядят, например, так:

(META)  [ST]S (partial) 02/08/2024 03/05/2024 $1,001 - $15,000

Я хотел бы добавить логику для включения деталей описания (например, «D:...»), если они существуют. Я попробовал использовать приведенный ниже шаблон, но в итоге он возвращает только одну большую транзакцию, поскольку первые две транзакции не имеют подробного описания (т. е. «D:»).

Я хотел бы увидеть это:

(META)  [ST]S (partial) 02/08/2024 03/05/2024 $1,001 - $15,000

..

(MSFT)  [ST]S (partial) 02/08/2024 03/05/2024 $1,001 - $15,000

..

(MSFT) [OP]P 02/13/2024 03/05/2024 $500,001 -
$1,000,000
F S: New
S O: Morgan Stanley - Portfolio Management Active Assets Account
D: Call options; Strike price $170; Expires 01/17 /2025

Что я делаю не так?

rx.findall(r"\([A-Z][^$]*\$[^$]*\$[,\d]+[\s\S]*?D:(.*)", text)

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

Чтобы справиться со случаями, когда двоеточие не смежно с буквой «D» (несовершенный анализ PDF), добавлено в ответ @zdim, и это решает вышеуказанные проблемы:

rx.findall('\([A-Z][^$]*\$[^$]*\$[,\d]+(?:[^$]*D:?.+)?', text)

Регулярное выражение может соответствовать только непрерывной строке. Поэтому, когда он соответствует первой строке (META) и строке D:, он должен включать в себя все, что находится между ними.

Barmar 03.05.2024 23:58

Лучшим методом может быть создание списка строк. Затем пройдитесь по строкам в обратном порядке. Когда вы увидите строку D:, продолжайте искать, пока не дойдете до строки транзакции, и объедините все эти строки. В противном случае, когда вы увидите строку транзакции, поместите ее в результат отдельно.

Barmar 04.05.2024 00:02

@Бармар, спасибо за это. Детали описания будут существовать только в том случае, если символы в квадратных скобках (например, [ST]) соответствуют «OP». Любой способ использовать эту логику для изменения вышеизложенного или разделения исходного текста на символы новой строки, вероятно, будет самым простым.

Chris 04.05.2024 00:14

В этом случае создайте одно регулярное выражение для однострочных транзакций, другое для серии строк между строкой [OP] и D:. Затем объедините их с |, чтобы они соответствовали одному или другому.

Barmar 04.05.2024 00:25
Почему в Python есть оператор "pass"?
Почему в Python есть оператор "pass"?
Оператор pass в Python - это простая концепция, которую могут быстро освоить даже новички без опыта программирования.
Некоторые методы, о которых вы не знали, что они существуют в Python
Некоторые методы, о которых вы не знали, что они существуют в Python
Python - самый известный и самый простой в изучении язык в наши дни. Имея широкий спектр применения в области машинного обучения, Data Science,...
Основы Python Часть I
Основы Python Часть I
Вы когда-нибудь задумывались, почему в программах на Python вы видите приведенный ниже код?
LeetCode - 1579. Удаление максимального числа ребер для сохранения полной проходимости графа
LeetCode - 1579. Удаление максимального числа ребер для сохранения полной проходимости графа
Алиса и Боб имеют неориентированный граф из n узлов и трех типов ребер:
Оптимизация кода с помощью тернарного оператора Python
Оптимизация кода с помощью тернарного оператора Python
И последнее, что мы хотели бы показать вам, прежде чем двигаться дальше, это
Советы по эффективной веб-разработке с помощью Python
Советы по эффективной веб-разработке с помощью Python
Как веб-разработчик, Python может стать мощным инструментом для создания эффективных и масштабируемых веб-приложений.
2
4
65
2
Перейти к ответу Данный вопрос помечен как решенный

Ответы 2

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

(\([A-Z]+\)[^$]*\$[^$]*\$[,\d]+)(?:(?:\n(?!.*\([A-Z]+\)[^$]*\$[^$]*\$[,\d]|D:).*)*\n(D:.*))?

Шаблон соответствует:

  • ( Захват группы 1
    • \([A-Z]+\) Сопоставьте ( 1+ символы от A до Z, а затем )
    • [^$]*\$[^$]*\$[,\d]+ Сопоставьте любой символ, кроме $, затем сопоставьте $, затем снова любой символ, кроме $, затем $, а затем 1+ раз либо ,, либо цифру.
  • ) Закрыть группу 1
  • (?: Группа без захвата
    • (?: Группа без захвата
      • \n Сопоставить новую строку
      • (?!.*\([A-Z]+\)[^$]*\$[^$]*\$[,\d]|D:) Негативный просмотр вперед: утверждается, что справа нет исходного шаблона или что линия начинается с D:
      • .* Сопоставьте всю строку
    • )* Закройте группу без захвата и при необходимости повторите.
    • \n(D:.*) Сопоставьте новую строку, затем запишите D: и остальную часть строки в группе 2.
  • )? Закройте группу без захвата и сделайте ее необязательной.

Посмотрите демонстрацию регулярных выражений и демонстрацию Python

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

Посмотрите еще одну демонстрацию регулярных выражений

Ответ принят как подходящий

Сделайте «остальную часть» после части транзакции (до строки D:... включительно) необязательной.

trans = re.findall(
    r'\([A-Z] [^$]* \$[^$]*\$[,\d]+ (?: [^$]* D:.+ )?',
    text, re.X)

for t in trans:
    print(t,'\n---')

Это печатает

(META) [ST]S (partial) 02/08/2024 03/05/2024 $1,001 - $15,000 
---
(MSFT) [ST]S (partial) 02/08/2024 03/05/2024 $1,001 - $15,000 
---
(MSFT) [OP]P 02/13/2024 03/05/2024 $500,001 -
$1,000,000
F S: New
S O: Morgan Stanley - Portfolio Management Active Assets Account
D: Call options; Strike price $170; Expires 01/17 /2025 

Теперь, если вам нужно дополнительно обработать этот дополнительный текст, это должно быть легко.

Потрясающе, спасибо! Добавлен один элемент для обработки других случаев, когда двоеточие не соседствует с «D» (несовершенный анализ PDF и все такое). Это дает мне то, что я ожидаю: rx.findall('\([A-Z][^$]*\$[^$]*\$[,\d]+(?:[^$]*D:?.+)?', text)

Chris 04.05.2024 21:12

Отлично :) Дайте мне знать, если возникнут вопросы или крайние случаи. Кстати, это та же проблема, что и та, которую вы задали несколько дней назад, на которую я опубликовал ответ? Если да, то этот вопрос немного проясняет этот вопрос :)

zdim 05.05.2024 04:47

@zdiim, это связано, да.

Chris 05.05.2024 21:40

Другие вопросы по теме