Я сопоставляю цифры в 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,.+-])|))"
Я не вижу причин, по которым вы не можете просто использовать 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
.
test
строке все выглядело хорошо, но это не позволяло копировать / вставлять. Я сделал это так, чтобы он работал на моей машине, и, надеюсь, на других. Я пропустил ' P
' перед ' <three_digits>
', когда копировал / вставлял с regex101.com - я добавил названные группы в попытке прояснить ситуацию. Спасибо за "чтение" этих ошибок и за ответ, который мне нужен!
#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.'
; Так же, как требуется.
re.sub(r"[, ]", "", match)
? Вы можете вызывать это только для строки, но не для объекта соответствия. И, даже если вы исправите это, как только выcorrected_match
, что вы собираетесь с ним делать? Очевидно, что ничто из того, что вы делаете для создания новой строки, никак не повлияет наtest
.re.sub
будет работать наmatch
. Я не был уверен, как сделать замену там - это был мой вопрос, @abarnert. Я не был уверен, как получить материал из матчей. Ваш ответ с лямбда-функциями делает именно то, что я хочу. Большое спасибо за ответ на несовершенный вопрос. То, что вы сделали, влияет на тестирование, чего я и хотел.