У меня есть текст, содержащий различные транзакции, которые я пытаюсь проанализировать с помощью регулярного выражения.
Текст выглядит следующим образом:
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)
Лучшим методом может быть создание списка строк. Затем пройдитесь по строкам в обратном порядке. Когда вы увидите строку D:
, продолжайте искать, пока не дойдете до строки транзакции, и объедините все эти строки. В противном случае, когда вы увидите строку транзакции, поместите ее в результат отдельно.
@Бармар, спасибо за это. Детали описания будут существовать только в том случае, если символы в квадратных скобках (например, [ST]) соответствуют «OP». Любой способ использовать эту логику для изменения вышеизложенного или разделения исходного текста на символы новой строки, вероятно, будет самым простым.
В этом случае создайте одно регулярное выражение для однострочных транзакций, другое для серии строк между строкой [OP]
и D:
. Затем объедините их с |
, чтобы они соответствовали одному или другому.
Вы можете использовать отрицательный просмотр вперед, а если вам нужно только описание, вы можете использовать вторую группу захвата в необязательной части:
(\([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)
Отлично :) Дайте мне знать, если возникнут вопросы или крайние случаи. Кстати, это та же проблема, что и та, которую вы задали несколько дней назад, на которую я опубликовал ответ? Если да, то этот вопрос немного проясняет этот вопрос :)
@zdiim, это связано, да.
Регулярное выражение может соответствовать только непрерывной строке. Поэтому, когда он соответствует первой строке
(META)
и строкеD:
, он должен включать в себя все, что находится между ними.