Check If String Contains a Number in Python

Introduction

Whether you’re building a verification script for user input, a login form that requests users to include a character in a password – checking whether a string contains a character isn’t an uncommon operation.

In this tutorial – we’ll take a look at the many ways you can check whether a string contains a digit/number in Python, including a benchmark for the most efficient approach in the end.

Check If String Contains Number in Python

There’s a multiple ways to check whether a character is a number (ord(), isnumeric(), isdigit()), which you can couple with a for-loop, to check for at least a single positive hit. Alternatively, you can use Regular Expressions as general pattern matchers, which are flexible, powerful and designed to be applied to large corpuses of text. Finally – you can always map() each character given a conditional statement, and return True is any() of them result in True.

Choosing between these should take into consideration the efficiency of the methods, verbosity and coding style, as well as upstream or downstream tasks associated with the operation.

Check if String Contains Number with ord()

The ord() function takes a character and returns its ASCII value:

print(ord('0')) 
print(ord('9')) 

The ASCII value of 0 is 48, and the ASCII value of 9 is 57. Any number between these, will by extension, have an ASCII value between 48 and 57. Now to check if the string has any number, we will traverse the whole input string and check the ASCII value of each character, if the ASCII value is more than 47 and less than 58, it means it’s a number, and we will return True:

input_string = "My name is Satyam & I am 22 yrs old"
flag = False
for ch in input_string:
    ascii_code = ord(ch)
    if 47 < ascii_code < 58:
        flag = True
        break
if flag:
    print("Yes, the string contains a number.")
else:
    print("No, the string does not contain a number.")

This results in:

Yes, the string contains a number.

Check if String Contains Number with isnumeric()

The isnumeric() function returns True if the input string contains only numbers, otherwise, it returns False:

str1 = "918"
print("String is whole numeric?", str1.isnumeric())
str2 = "The meaning of the universe is 42"
print("String is whole numeric?", str2.isnumeric())

This results in:

String is whole numeric? True 
String is whole numeric? False

Note: The isnumeric() function will not behave as you may expect for negative or float numbers. If we pass a string with only negative or float numbers, it will return False, because the - and . characters associated with negative numbers and floats are indeed, not numbers.

str1 = "-918"
print("String is whole numeric?", str1.isnumeric()) 

str2 = "91.8"
print("String is whole numeric?", str2.isnumeric()) 

Though, since characters are just strings of length 1 in Python – you can iterate through characters and use isnumeric() to check whether they’re a number:

input_string = "My name is Satyam & I am 22 yrs old"
flag = False
for ch in input_string:
    if ch.isnumeric():
        flag = True
        break
if flag:
    print("Yes, the string contains a number.")
else:
    print("No, the string does not contain a number.")

Check if String Contains Number with isdigit()

The isdigit() function checks whether all the characters in a string are digits. If yes – it returns True, and if not, it returns False. Again, since characters are just strings of length 1 in Python – this method can be used in a loop for each character:

input_string = "My name is Satyam & I am 22 yrs old"
flag = False
for ch in input_string:
    if ch.isdigit():
        flag = True
        break
if flag:
    print("Yes, the string contains a number.")
else:
    print("No, the string does not contain a number.")

Note: The isdigit() method only behaves in the same manner as isnumeric(), and if you pass a string containing a float or a negative number to it, False is returned due to the special characters not being numbers. On a character-level, though, if as long as one True value is enough to determine whether the string contains a number – it’s applicable.

Difference Between isnumeric() and isdigit()?

So, what’s the difference between isnumeric() and isdigit()? While we’re at it – what about isdecimal()?

  • isnumeric() checks whether any character is a unicode representation of a numeric value (which includes roman numeric representations, superscripts, subscripts and fractions)
  • isdigit() checks whether any character is a unicode digit (which doesn’t innclude roman numeric representations, but does include super/subscripts and fractions)
  • isdecimal() checks whether any characters is a decimal digit (which would return False for anything that’s not 0..9 in base 10)

isnumeric() is the most broad method, while isdecimal() is the most narrow between the three.

Check if String Contains Number with map() and any()

The map() function executes the provided function for each element of the iterable passed in the map function. Each element of an iterable is passed to the function as a parameter:

map(function, iterable)

The function is executed for every item of the iterable. This allows for very flexible and powerful logic, only bounded by the extensiveness of the function you call on the input! The method returns a map instance, which can be easily turned into other collections such as a list or set.

We can write a function that returns a boolean representing whether a character is a number, and the map() call will thus result in a list of boolean values.

The any() returns True if any element of the passed iterable is True, otherwise, it returns False.

Stringing these two together – we can create a high-level, short script and abstract the for-loop away:

def func(ch):
    return ch.isdigit() 

input_string = "My name is Satyam & I am 22 yrs old"
contains_number = any(list(map(func, input_string)))
print("Is there a number present?", contains_number)

This results in:

Is there a number present? True

If your function is a one-liner – there’s no need to extract it as a named function. You can write an anonymous lambda function instead for brevity’s sake:

Check out our hands-on, practical guide to learning Git, with best-practices, industry-accepted standards, and included cheat sheet. Stop Googling Git commands and actually learn it!

input_string = "My name is Satyam & I am 22 yrs old"
contains_number = any(list(map(lambda ch: ch.isdigit(), input_string)))
print("Is there any number present?", contains_number)

This also results in:

Is there any number present? True

Check if String Contains Number in Python with Regular Expressions

Regular Expressions are search patterns designed to be matched against input text. They’re flexible and given their nature – you can write an arbitrary number of expressions for the same pattern to search for, as well as cover any tractible pattern you can think of.

Python’s re module is used to write, compile and match text against regular expressions. It exposes various methods, such as match() which matches whether a string begins with a pattern, search() which finds the first occurrence of possibly many matches in a string, and findall() which checks for all occurences.

Note: All three methods accept a pattern and search argument and run a search for the pattern in the search string.

The pattern that identifies a digit is "d+":

import re
input_string = "My name is Satyam & I am 22 yrs old"
match = re.search(r"d+", input_string)
if match:
    print("Is there any number present?", "Yes")
else:
    print("Is there any number present?", "No")

The search() method returns a re.Match object, containing the match found and the starting and ending indices:


The object is can be evaluated to a boolean value based on whether it’s a re.Match object or None. This results in:

Is there any number present? Yes

Unlike the search() method, the findall() method returns all occurrences of the pattern instead of just the first one:

import re
input_string = "My name is Satyam & I am 22 yrs old"
match = re.findall(r"d+", input_string)
if match:
    print("Is there any number present?", "Yes")
else:
    print("Is there any number present?", "No")

This results in:

Is there any number present? Yes

Benchmarking

What about the performance? If you extract the logic and trim the unnecessary parts, limiting the methods to returning the result only, you can easily benchmark them one against the other on the same input:

%timeit ord_check()
%timeit isnumeric_check()
%timeit is_digit_check()
%timeit lambda_check()
%timeit regex_check()

This results in:

2.18 µs ± 51.5 ns per loop (mean ± std. dev. of 7 runs, 100,000 loops each)
2.04 µs ± 639 ns per loop (mean ± std. dev. of 7 runs, 1,000,000 loops each)
1.88 µs ± 448 ns per loop (mean ± std. dev. of 7 runs, 1,000,000 loops each)
5.07 µs ± 915 ns per loop (mean ± std. dev. of 7 runs, 100,000 loops each)
1.47 µs ± 3.41 ns per loop (mean ± std. dev. of 7 runs, 1,000,000 loops each)

Generally the for-loop approaches run in around the same time, with little overhead from the specific methods. Lambda with any() is defacto the slowest (a lot of reduntant operations, due to converting a list to a list and then reducing it), while Regular Expressions had the fastest runtime with the lowest variance around it (they’re consistently fast).

However, on longer input texts, the time complexities on each of the different approaches get emphasized, especially depending on the number of matched digits (whether digits are common or not):

import random
import string

input_string_random = ''.join(random.choices(string.ascii_uppercase + string.digits, k=1000))
print(input_string_random) 

input_string_with_single_digit = ''.join(random.choices(string.ascii_uppercase, k=1000)) + '1'
print(input_string_with_single_digit) 

The first string generates a random sequence with about an equal number of digits and characters, while the latter is a character-only string with a single digit in the end (worst time complexity):

%timeit ord_check(input_string_random)
504 ns ± 22.6 ns per loop (mean ± std. dev. of 7 runs, 1,000,000 loops each)
%timeit ord_check(input_string_with_single_digit)
76.2 µs ± 1.36 µs per loop (mean ± std. dev. of 7 runs, 10,000 loops each)
%timeit isnumeric_check(input_string_random)
756 ns ± 170 ns per loop (mean ± std. dev. of 7 runs, 1,000,000 loops each)
%timeit isnumeric_check(input_string_with_single_digit)
76.2 µs ± 8.43 µs per loop (mean ± std. dev. of 7 runs, 10,000 loops each)
%timeit is_digit_check(input_string_random)
549 ns ± 102 ns per loop (mean ± std. dev. of 7 runs, 1,000,000 loops each)
%timeit is_digit_check(input_string_with_single_digit)
65 µs ± 20.6 µs per loop (mean ± std. dev. of 7 runs, 10,000 loops each)
%timeit lambda_check(input_string_random)
114 µs ± 8.77 µs per loop (mean ± std. dev. of 7 runs, 10,000 loops each)
%timeit lambda_check(input_string_with_single_digit)
119 µs ± 6.23 µs per loop (mean ± std. dev. of 7 runs, 10,000 loops each)
%timeit regex_check(input_string_random)
996 ns ± 19.8 ns per loop (mean ± std. dev. of 7 runs, 1,000,000 loops each)
%timeit regex_check(input_string_with_single_digit)
22.2 µs ± 1.77 µs per loop (mean ± std. dev. of 7 runs, 10,000 loops each)

With a low number of hits – Regular Expressions are the most performant. With many hits, the lambda function approach is the most performant, and it retains its time complexity regardless of whether the input has many hits or one. The main downside (reduntant computation when hit rate is low) is turned into its main strength as redundancy makes it robust to input.

Conclusion

In this tutorial, we took a look at multiple ways to check whether a string in Python contains at least one character. We’ve taken a look at the ord(), isnumeric(), isdigit() and isdecimal() function, as well as how to abstract this logic with a lambda function call using map() and any(). Then, we explored Regular Expressions and benchmarked the approaches with varying input.

Time Stamp:

More from Stackabuse