после сопоставления. все экземпляры повторяющейся группы соответствия, python

1

Я сопоставляю цифры в str, используя регулярное выражение в Python. Мое желание состоит в том, чтобы записывать числа, которые могут иметь разделитель тысяч (для меня, запятая или пробел) или просто строка чисел. Ниже показано, что мое regex захватывает

>>> import re
>>> test = '3,254,236,948,348.884423 cold things, ' + \
'123,242 falling birds, .84973 of a French pen , ' + \
'65 243 turtle gloves, 8 001 457.2328009 units, and ' + \
'8d523c.'
>>> matches = re.finditer(ANY_NUMBER_SRCH, test, flags=re.MULTILINE)
>>> for match in matches:
...   print (str(match))
...
<_sre.SRE_Match object; span=(0, 24), match='3,254,236,948,348.884423'>
<_sre.SRE_Match object; span=(27, 34), match='123,242'>
<_sre.SRE_Match object; span=(37, 43), match='.84973'>
<_sre.SRE_Match object; span=(46, 52), match='65 243'>
<_sre.SRE_Match object; span=(55, 72), match='8 001 457.2328009'>
<_sre.SRE_Match object; span=(73, 74), match='8'>
<_sre.SRE_Match object; span=(75, 78), match='523'>

Это подходящее поведение, которое я хочу. Теперь я хочу взять каждый из сопоставленных номеров и удалить разделители тысяч (',' или ' '), если они существуют. Это должно оставить меня

'3254236948348.884423 cold things, ' + \
'123242 falling birds, .84973 of a French pen ,' + \
'65243 turtle gloves, 8001457.2328009 units, ' + \
'and 8d523c.'

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

Я хочу сделать следующее:

matches = some_method_to_get_all_matches(ANY_NUMBER_SRCH)
for match in matches:
  corrected_match = re.sub(r"[, ]", "", match)
  change_match_to_corrected_match_in_the_test_string

Как бы то ни было, я не могу использовать группы замещения. Если вы просто хотите увидеть регулярное выражение, вы можете проверить https://regex101.com/r/AzChEE/3. В основном часть моего регулярного выражения выглядит следующим образом

r"(?P<whole_number_w_thous_sep>(?P<first_group>\d{1,3})(?P<thousands_separator>[ ,])(?P<three_digits_w_sep>(?P<three_digits>\d{3})(?P=thousands_separator))*(?P<last_group_of_three>\d{3})(?!\d)"

Я буду представлять это без "строки прокрутки":

(r"(?P<whole_number_w_thous_sep>(?P<first_group>\d{1,3})"
  "(?P<thousands_separator>[ ,])"
  "(?P<three_digits_w_sep>(?P<three_digits>\d{3})"
  "(?P=thousands_separator))*"
  "(?P<last_group_of_three>\d{3})(?!\d)")

Механизм регулярных выражений не сохраняет повторяющийся three_digits_with_separator из-за * для повторных групп захвата.

Я уверен, что есть способ использовать span части _sre.SRE_Match object. Тем не менее, это будет очень сложно, и я имею дело со строками с тысячами до сотен тысяч символов. Есть ли простой способ сделать re.sub после re.match или re.iter или какой другой метод используется для поиска шаблона номера?

@abarnert получил правильный ответ - используя функцию лямбда. Мой комментарий в ответ на @abarnert, начиная с "Проверено!" показывает все этапы.


Мои попытки

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

>>> matches = re.finditer(lib_re.ANY_NUMBER_SRCH, test, flags=re.MULTILINE)     
>>> for match in matches:
...   print ("match: " + str(match))
...   corrected_match = re.sub(r"[, ]", "", match)
...   print ("corrected_match: " + str(corrected_match))
...
match: <_sre.SRE_Match object; span=(0, 24), match='3,254,236,948,348.884423'>
Traceback (most recent call last):
  File "<stdin>", line 3, in <module>
  File "/usr/lib/python3.6/re.py", line 191, in sub
    return _compile(pattern, flags).sub(repl, string, count)
TypeError: expected string or bytes-like object
>>>   print ("corrected_match: " + str(corrected_match))

Большое регулярное выражение

В случае, если что-то происходит с ссылкой regex101.com, вот гигантское регулярное выражение:

ANY_NUMBER_SRCH = r"(?P<number_capture>(?P<pre1>(?<![^0-9,.+-])|)(?P<number>(?P<sign_symbol_opt1>(?<![0-9])[+-])?(?P<whole_number_w_thous_sep>(?P<first_group>\d{1,3})(?P<thousands_separator>[ ,])(?P<three_digits_w_sep>(?P<three_digits>\d{3})(?P=thousands_separator))*(?P<last_group_of_three>\d{3})(?!\d)|(?P<whole_number_w_o_thous_sep>\d+))(?P<decimal_separator_1>[.])?(?P<fractional_w_whole_before>(?<=[.])(?P<digits_after_decimal_sep_1>\d+))?(?P<post1>(?<![^0-9,.+-])|)|(?P<pre2>(?<![^0-9,.+-])|)(?P<fractional_without_whole_before>(?P<sign_symbol_opt2>(?<![0-9])[+-])?(?P<decimal_separator_2>[.])(?P<digits_after_decimal_sep_2>\d+)))(?P<post2>(?<![^0-9,.+-])|))"
  • 1
    Между тем, что вы ожидали от re.sub(r"[, ]", "", match) ? Вы можете вызывать это только для строки, но не для объекта соответствия. И, даже если вы исправите это, как только вы corrected_match , что вы собираетесь с ним делать? Очевидно, что ничто из того, что вы делаете для создания новой строки, никак не повлияет на test .
  • 0
    Я не ожидал, что re.sub будет работать на match . Я не был уверен, как сделать замену там - это был мой вопрос, @abarnert. Я не был уверен, как получить материал из матчей. Ваш ответ с лямбда-функциями делает именно то, что я хочу. Большое спасибо за ответ на несовершенный вопрос. То, что вы сделали, влияет на тестирование, чего я и хотел.
Показать ещё 1 комментарий
Теги:
string
replace
string-matching

1 ответ

1
Лучший ответ

Я не вижу причин, по которым вы не можете просто использовать re.sub вместо re.finditer. Ваш repl применяется один раз для каждого совпадения, и возвращается результат замены каждого pattern на repl в string, что именно то, что вы хотите.

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

>>> test = '3,254,236,948,348.884423 cold things and 8d523c'
>>> pattern = re.compile(r'[\d,]+')
>>> pattern.findall(test) # just to verify that it works
['3,254,236,948,348', '884423', '8', '523']
>>> pattern.sub(lambda match: match.group().replace(',', ''), test)
'3254236948348.884423 cold things and 8d523c'

Очевидно, ваша repl функция будет немного сложнее, чем просто удаление всех запятых-и вы, вероятно, хотите def его вне линии, а не пытаться втиснуть его в lambda. Но независимо от вашего правило, если вы записать его как функцию, которая принимает match объект и возвращает строку, которую вы хотите вместо этого объекта матча, вы можете просто передать эту функцию к sub.

  • 0
    Я прошу прощения за то, что не перепроверил мой вопрос. Я пытался сделать так, чтобы в test строке все выглядело хорошо, но это не позволяло копировать / вставлять. Я сделал это так, чтобы он работал на моей машине, и, надеюсь, на других. Я пропустил ' P ' перед ' <three_digits> ', когда копировал / вставлял с regex101.com - я добавил названные группы в попытке прояснить ситуацию. Спасибо за "чтение" этих ошибок и за ответ, который мне нужен!
  • 0
    Проверено! #ANY_NUMBER_SRCH as in question; ; #test as in question; ; >>>pattern=re.compile(ANY_NUMBER_SRCH); ; test_corrected=pattern.sub(lambda match: match.group().replace(',', '').replace(' ', ''), test); ; >>>test_corrected; # result # '3254236948348.884423 cold things, 123242 falling birds, .84973 of a French pen , 65243 turtle gloves, 8001457.2328009 units, and 8d523c.' ; Так же, как требуется.

Ещё вопросы

Сообщество Overcoder
Наверх
Меню