I am creating a bot using the PyTelegramBotApi library. When I ask the user to enter a parameter, my goal is to accept if he only sends a message of type int or float, otherwise ask him to re-enter. When I use the code below, the bot receives and stores the int message sent by the user, but asks for re-entry if a float type message is entered.
import re
import telebot
from telebot import types
bot = telebot.TeleBot(TOKEN)
pattern = r'\d{1,7}\.\d'
def perimeter(message):
global p, pattern
if re.match(message.text, pattern) or message.text.isdigit():
p = float(message.text)
print('p:', p, type(p))
bot.send_message(message.chat.id, "Message saved")
else:
msg = bot.send_message(message.chat.id, "Please try again!")
bot.register_next_step_handler(msg, perimeter)
bot.infinity_polling()
CodePudding user response:
Since you will accept an int or float, why not define your regex to match an int or a float, something like:
pattern = r'\d (\.\d*)?$'
Then you can just use re.match()
and not have to do any other check. Either it matches or it doesn't.
Be sure to end your pattern with '$' as I show above, so that you don't accept a string that might starts with a valid int or float, but has additional junk characters afterward (like 12.34ZYX
).
Some other bits on your code:
@ewz93's comment about having the arguments to
re.match
reversed is correct. The pattern should come first, and the string being matched comes second.global pattern, p
is not necessary, at least looking at the code you posted. You do assign top
in your code, so you may be referencing it elsewhere later (but doing this through globals isn't the best Python practice).pattern
is only read in this code, so your function can access the module-levelpattern
without declaring it global. If your function assigned a value to the module-levelpattern
, then you would need to useglobal
.Having just created
p
asp = float(message.text)
, there shouldn't be any question about what typep
is from there, so printing outtype(p)
will always be "float". It is good to include some kind of type output when usingprint()
for debugging, often just doingprint(repr(p))
is sufficient -repr()
output usually includes some indication of the type (strs are in quotes, floats are formatted with decimal places, lists are shown in []'s, etc.). If you are using Python 3.6 or later, you could do this with an f-string like this:print(f"{p!r}")
. The trailing!r
is how you show that that value's repr should be displayed.
CodePudding user response:
You have the order of your arguments wrong,
re.match()
expects the pattern first and then text.re.match()
will be Trueif the pattern is anywhere in the stringif the string starts with the pattern. So with your current logic something like"ABC0.123XYZ"
"0.123XYZ"
would return True and then fail to get parsed.You should not do an extra check with
isdigit
, just include that case (optional.
in the number) in your pattern when you are doing regex anyway.Right now you limit float numbers to one digit after the
.
, is that really what you want? Reject anything with 2 or more decimal places?
Edit:
Since you did not really get an answer on how to make it work - you can easiely do this without regex:
import re
import telebot
from telebot import types
bot = telebot.TeleBot(TOKEN)
def perimeter(message):
global p
try:
p = float(message.text)
print('p:', p, type(p))
bot.send_message(message.chat.id, "Message saved")
except ValueError:
msg = bot.send_message(message.chat.id, "Please try again!")
bot.register_next_step_handler(msg, perimeter)
bot.infinity_polling()
If you want to do it with regex:
import re
import telebot
from telebot import types
bot = telebot.TeleBot(TOKEN)
pattern = "\d (.\d )?"
def perimeter(message):
global p, pattern
if re.fullmatch(pattern, message.text):
p = float(message.text)
print('p:', p, type(p))
bot.send_message(message.chat.id, "Message saved")
else:
msg = bot.send_message(message.chat.id, "Please try again!")
bot.register_next_step_handler(msg, perimeter)
bot.infinity_polling()
Also I don't think it is necessary to declare p
and pattern
as globals, since this would only do something if you change them and then use them again outside of the method and want to have the changed values. Even if you do, it is better to work with return values.