Home > Software engineering >  Regex PyTelegramBotApi float int type messages
Regex PyTelegramBotApi float int type messages

Time:03-13

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:

  1. @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.

  2. global pattern, p is not necessary, at least looking at the code you posted. You do assign to p 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-level pattern without declaring it global. If your function assigned a value to the module-level pattern, then you would need to use global.

  3. Having just created p as p = float(message.text), there shouldn't be any question about what type p is from there, so printing out type(p) will always be "float". It is good to include some kind of type output when using print() for debugging, often just doing print(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:

  1. You have the order of your arguments wrong, re.match() expects the pattern first and then text.

  2. re.match() will be True if the pattern is anywhere in the string if 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.

  3. 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.

  4. 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.

  • Related