Sprawdź, czy ciąg zawiera liczbę w Pythonie

Wprowadzenie

Niezależnie od tego, czy tworzysz skrypt weryfikacyjny do wprowadzania danych przez użytkownika, formularz logowania, który prosi użytkowników o umieszczenie znaku w haśle – sprawdzanie, czy ciąg znaków zawiera znak, nie jest rzadką operacją.

W tym samouczku przyjrzymy się wielu sposobom sprawdzenia, czy ciąg zawiera cyfrę/liczbę w Pythonie, w tym benchmarkowi dla najbardziej wydajnego podejścia.

Sprawdź, czy ciąg zawiera liczbę w Pythonie

Istnieje wiele sposobów sprawdzenia, czy a charakter jest liczbą (ord(), isnumeric(), isdigit()), które można połączyć z pętlą for, aby sprawdzić przynajmniej jedno pozytywne trafienie. Alternatywnie możesz użyć wyrażeń regularnych jako ogólnych elementów dopasowujących wzorce, które są elastyczne, wydajne i zaprojektowane do stosowania w dużych korpusach tekstu. Wreszcie – zawsze można map() każdy znak otrzymał instrukcję warunkową i zwraca True is any() z nich skutkuje True.

Wybierając między nimi, należy wziąć pod uwagę efektywność metod, gadatliwość i styl kodowania, a także zadania poprzedzające lub następcze związane z operacją.

Sprawdź, czy ciąg zawiera liczbę za pomocą ord ()

Połączenia ord() funkcja przyjmuje znak i zwraca jego ASCII wartość:

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

Wartość ASCII 0 wynosi 48, a wartość ASCII 9 wynosi 57. Dowolna liczba między nimi będzie, co za tym idzie, mieć wartość ASCII między 48 a 57. Teraz, aby sprawdzić, czy łańcuch ma jakąkolwiek liczbę, przejdziemy przez cały ciąg wejściowy i sprawdzimy wartość ASCII każdego znaku, jeśli wartość ASCII jest większa niż 47 i mniejsza niż 58, oznacza to, że jest to liczba, i wrócimy 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.")

To skutkuje:

Yes, the string contains a number.

Sprawdź, czy ciąg zawiera liczbę z isnumeric ()

Połączenia isnumeric() funkcja zwraca True jeśli ciąg wejściowy zawiera tylko liczby, w przeciwnym razie zwraca 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())

To skutkuje:

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

Uwaga: Połączenia isnumeric() funkcja nie będzie zachowywać się tak, jak można się spodziewać liczby ujemne lub zmiennoprzecinkowe. Jeśli przekażemy ciąg zawierający tylko liczby ujemne lub zmiennoprzecinkowe, powróci False, ponieważ - i . znaki związane z liczbami ujemnymi i liczbami zmiennoprzecinkowymi rzeczywiście nie są liczbami.

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

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

Chociaż, ponieważ znaki są po prostu łańcuchami o długości 1 w Pythonie – możesz iterować po znakach i używać isnumeric() aby sprawdzić, czy są liczbą:

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.")

Sprawdź, czy ciąg zawiera liczbę za pomocą isdigit ()

Połączenia isdigit() Funkcja sprawdza, czy wszystkie znaki w łańcuchu są cyframi. Jeśli tak – powraca True, a jeśli nie, to zwraca False. Ponownie, ponieważ znaki są w Pythonie po prostu łańcuchami o długości 1 – ta metoda może być używana w pętli dla każdego znaku:

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.")

Uwaga: Połączenia isdigit() metoda zachowuje się tylko w taki sam sposób jak isnumeric(), a jeśli przekażesz do niego ciąg znaków zawierający liczbę zmiennoprzecinkową lub liczbę ujemną, False jest zwracany, ponieważ znaki specjalne nie są liczbami. Jednak na poziomie postaci, jeśli tak długo, jak jeden True wartość wystarczy do określenia, czy ciąg znaków zawiera liczbę – ma zastosowanie.

Różnica między isnumeric() a isdigit()?

Więc jaka jest różnica między isnumeric() i isdigit()? Skoro już przy tym jesteśmy – o co chodzi isdecimal()?

  • isnumeric() sprawdza, czy jakikolwiek znak jest a reprezentacja Unicode z wartość numeryczna (co obejmuje reprezentacje liczb rzymskich, indeksy górne, indeksy dolne i ułamki zwykłe)
  • isdigit() sprawdza, czy jakikolwiek znak jest a cyfra Unicode (co nie obejmuje rzymskich reprezentacji liczbowych, ale zawiera indeksy górne/dolne i ułamki)
  • isdecimal() sprawdza, czy któryś ze znaków to a cyfra dziesiętna (która wróci False za wszystko, co nie jest 0..9 w podstawie 10)

isnumeric() jest najszerszą metodą, podczas gdy isdecimal() jest najwęższa spośród tych trzech.

Sprawdź, czy ciąg zawiera numer z map() i any()

Połączenia map() funkcja wykonuje podaną funkcję dla każdego elementu iterowalnego przekazanego w funkcji mapy. Każdy element elementu iterowalnego jest przekazywany do funkcji jako parametr:

map(function, iterable)

Połączenia function jest wykonywany dla każdego elementu iterable. Pozwala to na bardzo elastyczną i potężną logikę, ograniczoną jedynie przez rozległość function dzwonisz na wejście! Metoda zwraca a map przykład, który można łatwo przekształcić w inne kolekcje, takie jak lista lub zestaw.

Możemy napisać funkcję, która zwraca wartość logiczną reprezentującą, czy znak jest liczbą, oraz map() call spowoduje zatem wyświetlenie listy wartości boolowskich.

Połączenia any() powraca True jeśli jakikolwiek element przekazanej iterowalności jest True, w przeciwnym razie zwraca False.

Łącząc te dwa elementy razem – możemy stworzyć krótki skrypt wysokiego poziomu i wyabstrahować pętlę for:

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)

To skutkuje:

Is there a number present? True

Jeśli twoja funkcja jest jednowierszowa – nie ma potrzeby wyodrębniania jej jako nazwanej funkcji. Możesz napisać anonim funkcja lambda zamiast tego dla zwięzłości:

Zapoznaj się z naszym praktycznym, praktycznym przewodnikiem dotyczącym nauki Git, zawierającym najlepsze praktyki, standardy przyjęte w branży i dołączoną ściągawkę. Zatrzymaj polecenia Google Git, a właściwie uczyć się to!

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)

Powoduje to również:

Is there any number present? True

Sprawdź, czy ciąg zawiera liczbę w Pythonie z wyrażeniami regularnymi

Wyrażenia regularne są wzorce wyszukiwania zaprojektowane tak, aby pasowały do ​​tekstu wejściowego. Są elastyczne i ze względu na swoją naturę — ​​możesz napisać dowolną liczbę wyrażeń dla tego samego wzorca do wyszukania, a także objąć każdy możliwy do wykonania wzór, jaki przyjdzie ci do głowy.

Pythona re służy do pisania, kompilowania i dopasowywania tekstu do wyrażeń regularnych. Przedstawia różne metody, np match() który pasuje do tego, czy łańcuch zaczyna się od wzorca, search() który znajduje pierwsze wystąpienie prawdopodobnie wielu dopasowań w łańcuchu i findall() który sprawdza wszystkie wystąpienia.

Uwaga: Wszystkie trzy metody akceptują a pattern i search argument i uruchom wyszukiwanie pattern search ciąg.

Wzór, który identyfikuje a cyfra 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")

Połączenia search() metoda zwraca plik re.Match obiekt, zawierający znalezione dopasowanie oraz indeksy początkowe i końcowe:


Obiekt is można ocenić na wartość logiczną na podstawie tego, czy jest to a re.Match obiekt lub None. To skutkuje:

Is there any number present? Yes

w przeciwieństwie do search() metoda findall() Metoda zwraca wszystkie wystąpienia wzorca zamiast tylko pierwszego:

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")

To skutkuje:

Is there any number present? Yes

Benchmarking

A co z występem? Jeśli wyodrębnisz logikę i przytniesz niepotrzebne części, ograniczając metody tylko do zwrócenia wyniku, możesz łatwo porównać je ze sobą na tym samym wejściu:

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

To skutkuje:

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)

Ogólnie rzecz biorąc, podejścia z pętlą for działają mniej więcej w tym samym czasie, z niewielkim obciążeniem wynikającym z określonych metod. Lambda z any() jest de facto najwolniejszy (wiele zbędnych operacji, ze względu na konwersję listy na listę, a następnie jej zmniejszenie), podczas gdy wyrażenia regularne miały najszybszy czas działania z najniższą wariancją wokół niego (są konsekwentnie szybkie).

Jednak w przypadku dłuższych tekstów wejściowych podkreśla się złożoność czasową w każdym z różnych podejść, zwłaszcza w zależności od liczby dopasowanych cyfr (czy cyfry są wspólne, czy nie):

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) 

Pierwszy ciąg generuje losową sekwencję z mniej więcej taką samą liczbą cyfr i znaków, podczas gdy drugi to ciąg znaków z pojedynczą cyfrą na końcu (najgorsza złożoność czasowa):

%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)

Przy niskiej liczbie trafień – wyrażenia regularne są najbardziej wydajne. Przy wielu trafieniach podejście oparte na funkcji lambda jest najbardziej wydajne i to zachowuje swoją złożoność czasową niezależnie od tego, czy wejście ma wiele trafień, czy jedno. Główna wada (nadmiarowe obliczenia, gdy wskaźnik trafień jest niski) staje się jego główną zaletą, ponieważ nadmiarowość sprawia, że ​​jest odporny na wprowadzanie danych.

Wnioski

W tym samouczku przyjrzeliśmy się wielu sposobom sprawdzania, czy ciąg znaków w Pythonie zawiera co najmniej jeden znak. Przyjrzeliśmy się ord(), isnumeric(), isdigit() i isdecimal() function, a także jak wyodrębnić tę logikę za pomocą wywołania funkcji lambda using map() i any(). Następnie zbadaliśmy wyrażenia regularne i porównaliśmy podejścia z różnymi danymi wejściowymi.

Znak czasu:

Więcej z Nadużycie stosu