#python #python-3.x #regex #regex-lookarounds #re
#python #python-3.x #регулярное выражение #регулярное выражение -поиск #python-re
Вопрос:
Контекст и объяснение
Я создаю telegram-бота, и я хочу добавить символ ""
исключения перед каждым "_"
символом, которого нет в имени пользователя (слово, начинающееся с "@"
), например "@username_"
, чтобы предотвратить некоторые ошибки уценки (на самом деле в telegram "_"
символ используется для выделения строки курсивом).
Так, например, имея эту строку:
"hello i like this char _ write me lol_ @myusername_"
я хочу, чтобы мне соответствовали только первые два "_"
символа, но не третий
Вопрос
как правильно сделать это с помощью шаблона регулярных выражений?
Ожидаемые условия и соответствие
Условие | Совпадение |
---|---|
"_" один: ( "_" ) |
ДА |
"_" одним словом, без "@" : ( "lol_" ) |
ДА |
"_" в слове, начинающемся с "@" : ( "@username_" ) |
НЕТ |
"_" в слове, содержащем "@" после "@" : ( "lol@username_" ) |
НЕТ |
"_" в слове, содержащем "@" перед "@" : ( "lol_@username" ) |
ДА |
"_" в мире, подобном: ( "lol_@username_" ) |
первый: ДА, второй: НЕТ |
Что я пробовал
до сих пор я пришел к этому, но это не работает должным образом:
"(?=[^@] )(?:s[^s]*(_)[^s]*s)"
Редактировать
Я также хочу, чтобы в этой строке: "lol_@username_"
был сопоставлен первый символ "_"
Комментарии:
1.
(?:^|s)((?:[^@s]*?)_(?:[^@s]*?))(?:s|$)
2. @OlvinRoght спасибо, но здесь это не работает при удалении
"@"
3. @OlvinRoght Достаточно только последнего бита:
(?:^|s)((?:[^@s]*?)_(?:[^@s]*?))(?=s|$)
( например, так )4. Вы извлекаете или заменяете? Итак, у вас есть
"hello i like this char _ write me lol_ @myusername_"
, каков ожидаемый результат?5. @LeonardoScotti Это работает
Ответ №1:
Вы можете сопоставить все символы, не являющиеся пробелами, после сопоставления @
и записать _
их в группу, используя чередование. Если обратный вызов re.sub, проверьте, существует ли группа 1.
Если это так, верните экранированное подчеркивание или исключенное значение группы 1 (которое также является подчеркиванием), иначе верните совпадение, чтобы оставить его неизменным.
@S |(_)
Демонстрация регулярных выражений
import re
strings = [
"_",
"lol_",
"@username_",
"lol@username_",
"lol_@username",
"lol_@username_"
]
for s in strings:
result = re.sub(
r"@S |(_)",
lambda x: x.group(1).replace("_", r"_") if x.group(1) else x.group(),
s
)
print(result)
Вывод
_
lol_
@username_
lol@username_
lol_@username
lol_@username_
Ответ №2:
Основываясь на комментарии @OlvinRoght, с небольшой правкой, это должно сработать:
Регулярное выражение
((?:^|s)(?:[^@s]*?))(_)((?:[^@s]*?))(?=@|s|$)
Пример кода
import re
text = '_hi hello i like this char _ write me lol_ _word something_ @myusername_ something_@username_'
regex = r"((?:^|s)(?:[^@s]*?))(_)((?:[^@s]*?))(?=@|s|$)"
# Leave the first and last capturing group as-is and replace the underscore with '_'
subst = "\1\\_\3"
print( re.sub(regex, subst, text) )
Ожидаемый результат:
_hi hello i like this char _ write me lol_ _word something_ @myusername_ something_@username_
ДЕМОНСТРАЦИЯ
Примечание:
Хотя это работает, ответ @TheFourthBird быстрее. (И, по-моему, более элегантно.)
Ответ №3:
Извлечение с помощью библиотеки регулярных выражений PyPI:
import regex
string = "hello i like this char _ write me lol_ @myusername_"
print(regex.findall(r'(?<!S)@w (*SKIP)(*F)|_', string))
# ['_', '_']
Смотрите Доказательство Python.
Объяснение
--------------------------------------------------------------------------------
(?<! look behind to see if there is not:
--------------------------------------------------------------------------------
S non-whitespace (all but n, r, t, f,
and " ")
--------------------------------------------------------------------------------
) end of look-behind
--------------------------------------------------------------------------------
@ '@'
--------------------------------------------------------------------------------
w word characters (a-z, A-Z, 0-9, _) (1 or
more times (matching the most amount possible))
--------------------------------------------------------------------------------
(*SKIP)(*F) skip the match, search from the failure location
--------------------------------------------------------------------------------
| or
--------------------------------------------------------------------------------
_ a '_' char
Удалить с помощью re
:
import re
string = "hello i like this char _ write me lol_ @myusername_"
print(re.sub(r'(?<!S)(@w )|_', r'1', string))
# hello i like this char write me lol @myusername_
Смотрите Доказательство Python.
Заменить на re
:
import re
string = "hello i like this char _ write me lol_ @myusername_"
print(re.sub(r'(?<!S)(@w )|_', lambda x: x.group(1) or "-", string))
# hello i like this char - write me lol- @myusername_
Смотрите другое доказательство Python.
Комментарии:
1. если вы измените строку замены с
"-"
наr"_"
, то третий ответ будет выполнять то, что запросил OP … предполагая, что имена пользователей telegram содержат только символы wordw
2. @LeonardoScotti Почему вы тестируете шаблон регулярных выражений PyPI в pythex? Вы
import regex
(после установкиpip install regex
)? Вы должны протестировать его с включенной опцией PCRE, см. Эту демонстрацию регулярных выражений.3. извините, я заметил и удалил комментарий
4. третье работает хорошо, но я хочу, чтобы, например, в
"lol_@username_"
первом"_"
было сопоставлено, а второе — нет5. @LeonardoScotti Изменение вопроса сейчас в целом не является хорошей идеей, это делает недействительными текущие ответы. Вы можете удалить
(?<!S)
и использовать@w (*SKIP)(*F)|_
, см. Эту демонстрацию регулярных выражений.
Ответ №4:
Я предполагаю, что вы заботитесь только о @
том, чтобы быть в начале слова. Вы можете использовать re.sub
вместе с replace
и (?:s|^)[^@]S b
для сопоставления слов, которые соответствуют вашей спецификации:
import re
s = "hello i like this char _ write me lol_ @myusername_ asd@_a @_asdf"
s = re.sub(r"(?:s|^)[^@]S*b", lambda x: x.group().replace("_", r"_"), s)
print(s) # => hello i like this char _ write me lol_ @myusername_ asd@_a @_asdf
Если вы хотите @
появиться где-нибудь в word, попробуйте (?:s|^)[^@s] b
:
s = "he_llo i like this char _ write me lol_ @myusername_ asd@_a @_asdf"
s = re.sub(r"(?:s|^)[^@s] b", lambda x: x.group().replace("_", r"_"), s)
print(s) # => he_llo i like this char _ write me lol_ @myusername_ asd@_a @_asdf
Согласно комментарию OP, похоже, что последняя спецификация заключается в экранировании _
, которые находятся где угодно, кроме как после @
в слове:
>>> s = "he_llo i lol_@username_ _ write me lol_ @myusername_ asd@_a @_asdf"
>>> re.sub(r"(?:s|^)[^@] @", lambda x: x.group().replace("_", r"_"), s)
'he\_llo i lol\_@username_ \_ write me lol\_ @myusername_ asd@_a @_asdf'
Комментарии:
1. это работает хорошо, но я хочу, чтобы, например, в
"lol_@username_"
первом"_"
было сопоставлено, а во втором — нет2. Обновлено, хотя у вас уже есть другие ответы. Похоже, это не очень хорошо соответствует вашему названию, хотя «(слово, начинающееся с @)». Рекомендуется заранее добавить все ваши требования, чтобы избежать догадок.