Как проверить, является ли строка числом (float)?

каков наилучший способ проверить, может ли строка быть представлена как число в Python?

функция, которую я в настоящее время имею прямо сейчас:

def is_number(s):
    try:
        float(s)
        return True
    except ValueError:
        return False

что не только уродливо и медленно, кажется неуклюжим. Однако я не нашел лучшего метода, потому что вызов float в основной функции еще хуже.

30 ответов


что не только уродливо и медленно

Я бы оспорил оба.

регулярное выражение или другой синтаксический анализ строк были бы уродливее и медленнее.

Я не уверен, что что-то может быть быстрее, чем выше. Он вызывает функцию и возвращает. Try / Catch не вводит много накладных расходов, потому что наиболее распространенное исключение ловится без обширного поиска кадров стека.

проблема в том, что любая числовая функция преобразования два вида результатов

  • число, если число действительно
  • код состояния (например, через errno) или исключение, чтобы показать, что действительный номер не может быть проанализирован.

C (в качестве примера) хаки вокруг этого несколькими способами. Python излагает это ясно и явно.

Я думаю, что ваш код для этого идеально подходит.


Если вы ищете для разбора (положительные, без знака) целые числа вместо поплавков, вы можете использовать isdigit() функции для строковых объектов.

>>> a = "03523"
>>> a.isdigit()
True
>>> b = "963spam"
>>> b.isdigit()
False

String Методы - isdigit()

есть также что-то в строках Unicode, с которыми я не слишком хорошо знаком Unicode-is decimal / decimal


TL; DR лучшим решением s.replace('.','',1).isdigit()

Я сделал несколько критерии сравнение различных подходов

def is_number_tryexcept(s):
    """ Returns True is string is a number. """
    try:
        float(s)
        return True
    except ValueError:
        return False

import re    
def is_number_regex(s):
    """ Returns True is string is a number. """
    if re.match("^\d+?\.\d+?$", s) is None:
        return s.isdigit()
    return True


def is_number_repl_isdigit(s):
    """ Returns True is string is a number. """
    return s.replace('.','',1).isdigit()

если строка не является числом, блок except-довольно медленный. Но что еще более важно, метод try-except является единственным подходом, который правильно обрабатывает научные обозначения.

funcs = [
          is_number_tryexcept, 
          is_number_regex,
          is_number_repl_isdigit
          ]

a_float = '.1234'

print('Float notation ".1234" is not supported by:')
for f in funcs:
    if not f(a_float):
        print('\t -', f.__name__)

Float notation ".1234" не поддерживается:
- is_number_regex

scientific1 = '1.000000e+50'
scientific2 = '1e50'


print('Scientific notation "1.000000e+50" is not supported by:')
for f in funcs:
    if not f(scientific1):
        print('\t -', f.__name__)




print('Scientific notation "1e50" is not supported by:')
for f in funcs:
    if not f(scientific2):
        print('\t -', f.__name__)

научная обозначение "1.000000 e+50" не поддерживается:
- is_number_regex
- is_number_repl_isdigit
Научная нотация "1e50" не поддерживается:
- is_number_regex
- is_number_repl_isdigit

EDIT: результаты тестов

import timeit

test_cases = ['1.12345', '1.12.345', 'abc12345', '12345']
times_n = {f.__name__:[] for f in funcs}

for t in test_cases:
    for f in funcs:
        f = f.__name__
        times_n[f].append(min(timeit.Timer('%s(t)' %f, 
                      'from __main__ import %s, t' %f)
                              .repeat(repeat=3, number=1000000)))

где были протестированы следующие функции

from re import match as re_match
from re import compile as re_compile

def is_number_tryexcept(s):
    """ Returns True is string is a number. """
    try:
        float(s)
        return True
    except ValueError:
        return False

def is_number_regex(s):
    """ Returns True is string is a number. """
    if re_match("^\d+?\.\d+?$", s) is None:
        return s.isdigit()
    return True


comp = re_compile("^\d+?\.\d+?$")    

def compiled_regex(s):
    """ Returns True is string is a number. """
    if comp.match(s) is None:
        return s.isdigit()
    return True


def is_number_repl_isdigit(s):
    """ Returns True is string is a number. """
    return s.replace('.','',1).isdigit()

enter image description here


есть одно исключение, которое вы можете принять во внимание: строка 'NaN'

Если вы хотите, чтобы is_number возвращал FALSE для 'NaN', этот код не будет работать, поскольку Python преобразует его в представление числа, которое не является числом (поговорим о проблемах идентификации):

>>> float('NaN')
nan

в противном случае я должен поблагодарить вас за фрагмент кода, который я сейчас широко использую. :)

г.


как насчет этого:

'3.14'.replace('.','',1).isdigit()

который вернет true, только если есть один или нет". в строку цифр.

'3.14.5'.replace('.','',1).isdigit()

возвращает false

edit: просто увидел еще один комментарий ... добавление .replace(badstuff,'',maxnum_badstuff) для других случаев можно сделать. если вы передаете соль, а не произвольные приправы (ref:xkcd#974) это будет хорошо: P


Обновлено после того, как Alfe указал, что вам не нужно проверять поплавок отдельно, как сложные ручки:

def is_number(s):
    try:
        complex(s) # for int, long, float and complex
    except ValueError:
        return False

    return True

ранее сказано: в некоторых редких случаях вам также может потребоваться проверить наличие комплексных чисел (например, 1+2i), которые не могут быть представлены поплавком:

def is_number(s):
    try:
        float(s) # for int, long and float
    except ValueError:
        try:
            complex(s) # for complex
        except ValueError:
            return False

    return True

что не только уродливо и медленно, кажется неуклюжим.

может потребоваться некоторое привыкание, но это питонический способ сделать это. Как уже отмечалось, альтернативы хуже. Но есть еще одно преимущество такого подхода: полиморфизм.

основная идея duck typing заключается в том, что "если он ходит и говорит как утка, то это утка."Что делать, если вы решите, что вам нужно подкласс string, чтобы вы могли измените, как вы определяете, может ли что-то быть преобразовано в float? Или что, если вы решите проверить какой-то другой объект полностью? Вы можете делать эти вещи без необходимости изменять приведенный выше код.

другие языки решают эти проблемы с помощью интерфейсов. Я сохраню анализ того, какое решение лучше для другого потока. Дело в том, что python определенно находится на стороне ввода утки в уравнении, и вам, вероятно, придется привыкнуть к такому синтаксису, если вы планируете много программировать на Python (но это не значит, что вам это нравится, конечно).

еще одна вещь, которую вы можете принять во внимание: Python довольно быстро бросает и ловит исключения по сравнению со многими другими языками (например, 30x быстрее, чем .Net). Черт возьми, сам язык даже бросает исключения для связи не исключительных, нормальных условий программы (каждый раз, когда вы используете цикл for). Таким образом, я не стал бы слишком беспокоиться о аспекты производительности этого кода, пока вы не заметите существенную проблему.


на int используйте этот:

>>> "1221323".isdigit()
True

но float нам нужны некоторые хитрости ;-). Каждый номер поплавка имеет одну точку...

>>> "12.34".isdigit()
False
>>> "12.34".replace('.','',1).isdigit()
True
>>> "12.3.4".replace('.','',1).isdigit()
False

также для отрицательных чисел просто добавить lstrip():

>>> '-12'.lstrip('-')
'12'

и теперь мы получаем универсальный способ:

>>> '-12.34'.lstrip('-').replace('.','',1).isdigit()
True
>>> '.-234'.lstrip('-').replace('.','',1).isdigit()
False

для строк не-чисел,try: except: на самом деле медленнее, чем регулярные выражения. Для строк допустимых чисел регулярное выражение выполняется медленнее. Таким образом, соответствующий метод зависит от вашего ввода.

если вы обнаружите, что находитесь в привязке производительности, вы можете использовать новый сторонний модуль под названием fastnumbers это обеспечивает функцию под названием isfloat. Полное раскрытие, я автор. Я включил его результаты в тайминги под.


from __future__ import print_function
import timeit

prep_base = '''\
x = 'invalid'
y = '5402'
z = '4.754e3'
'''

prep_try_method = '''\
def is_number_try(val):
    try:
        float(val)
        return True
    except ValueError:
        return False

'''

prep_re_method = '''\
import re
float_match = re.compile(r'[-+]?\d*\.?\d+(?:[eE][-+]?\d+)?$').match
def is_number_re(val):
    return bool(float_match(val))

'''

fn_method = '''\
from fastnumbers import isfloat

'''

print('Try with non-number strings', timeit.timeit('is_number_try(x)',
    prep_base + prep_try_method), 'seconds')
print('Try with integer strings', timeit.timeit('is_number_try(y)',
    prep_base + prep_try_method), 'seconds')
print('Try with float strings', timeit.timeit('is_number_try(z)',
    prep_base + prep_try_method), 'seconds')
print()
print('Regex with non-number strings', timeit.timeit('is_number_re(x)',
    prep_base + prep_re_method), 'seconds')
print('Regex with integer strings', timeit.timeit('is_number_re(y)',
    prep_base + prep_re_method), 'seconds')
print('Regex with float strings', timeit.timeit('is_number_re(z)',
    prep_base + prep_re_method), 'seconds')
print()
print('fastnumbers with non-number strings', timeit.timeit('isfloat(x)',
    prep_base + 'from fastnumbers import isfloat'), 'seconds')
print('fastnumbers with integer strings', timeit.timeit('isfloat(y)',
    prep_base + 'from fastnumbers import isfloat'), 'seconds')
print('fastnumbers with float strings', timeit.timeit('isfloat(z)',
    prep_base + 'from fastnumbers import isfloat'), 'seconds')
print()

Try with non-number strings 2.39108395576 seconds
Try with integer strings 0.375686168671 seconds
Try with float strings 0.369210958481 seconds

Regex with non-number strings 0.748660802841 seconds
Regex with integer strings 1.02021503448 seconds
Regex with float strings 1.08564686775 seconds

fastnumbers with non-number strings 0.174362897873 seconds
fastnumbers with integer strings 0.179651021957 seconds
fastnumbers with float strings 0.20222902298 seconds

Как видите,

  • try: except: был быстрым для числового ввода, но очень медленным для недопустимого ввода
  • регулярное выражение очень эффективно, когда вход недопустим
  • fastnumbers выигрывает в обоих случаях

Просто Имитируйте C#

в C# есть две разные функции, которые обрабатывают разбор скалярных значений:

  • поплавок.Parse ()
  • поплавок.TryParse ()

поплавок.parse ():

def parse(string):
    try:
        return float(string)
    except Exception:
        throw TypeError

Примечание: Если вам интересно, почему я изменил исключение на TypeError,здесь документация.

поплавок.try_parse():

def try_parse(string, fail=None):
    try:
        return float(string)
    except Exception:
        return fail;

Примечание: Вы не хотите возвращать логическое значение "False", потому что это все еще тип значения. Нет ничего лучше, потому что это указывает на неудачу. Конечно, если вы хотите что-то другое, вы можете изменить параметр не то, что вы хотите.

чтобы расширить float, чтобы включить "parse ()" и " try_parse ()", вам нужно будет monkeypatch класс "float", чтобы добавить их методы.

если вы хотите уважать уже существующие функции код должен быть что-то вроде:

def monkey_patch():
    if(!hasattr(float, 'parse')):
        float.parse = parse
    if(!hasattr(float, 'try_parse')):
        float.try_parse = try_parse

SideNote: я лично предпочитаю называть это обезьяной, потому что мне кажется, что я злоупотребляю языком, когда я это делаю, но YMMV.

использование:

float.parse('giggity') // throws TypeException
float.parse('54.3') // returns the scalar value 54.3
float.tryParse('twank') // returns None
float.tryParse('32.2') // returns the scalar value 32.2

и великий мудрец Pythonas сказал Святейший Престол Sharpisus, "все, что вы можете сделать я могу сделать лучше, я могу сделать ничего лучше, чем вы."


Я знаю, что это особенно старый, но я бы добавил ответ, я считаю, охватывает информацию, отсутствующую в самом высоком голосовании ответа, который может быть очень ценным для любого, кто найдет это:

для каждого из следующих методов соедините их с подсчетом, если вам нужно, чтобы какой-либо вход был принят. (Предполагая, что мы используем вокальные определений целых чисел, а не 0-255, и т. д.)

x.isdigit() хорошо работает для проверки, если x-целое число.

x.replace('-','').isdigit() хорошо работает для проверка, является ли x отрицательным.(Регистрация в первой позиции)

x.replace('.','').isdigit() хорошо работает для проверки, является ли x десятичным.

x.replace(':','').isdigit() хорошо работает для проверки, является ли x отношением.

x.replace('/','',1).isdigit() хорошо работает для проверки, является ли x дробью.


Casting to float и catching ValueError, вероятно, самый быстрый способ, так как float() специально предназначен именно для этого. Все остальное, что требует синтаксического анализа строк (regex и т. д.), вероятно, будет медленнее из-за того, что оно не настроено для этой операции. Мои $0.02.


вы можете использовать строки Unicode, у них есть метод, чтобы сделать именно то, что вы хотите:

>>> s = u"345"
>>> s.isnumeric()
True

или:

>>> s = "345"
>>> u = unicode(s)
>>> u.isnumeric()
True

http://www.tutorialspoint.com/python/string_isnumeric.htm

http://docs.python.org/2/howto/unicode.html


Я хотел посмотреть, какой метод самый быстрый. В целом лучшие и наиболее последовательные результаты были даны


допустим, у вас есть цифры в строке. str = " 100949" и вы хотели бы проверить, имеет ли он только номера

if str.isdigit():
returns TRUE or FALSE 

isdigit docs

в противном случае ваш метод отлично работает, чтобы найти появление цифры в строке.


этот ответ предоставляет пошаговое руководство, имеющее функцию с примерами, чтобы найти строку:

  • положительное целое число
  • положительный / отрицательный-целое число / float
  • как отбросить строки" NaN " (не число) при проверке номера?

проверьте, если строка положительное целое

вы можете использовать str.isdigit() чтобы проверить, является ли заданная строка -положительное целое число.

Образец Результаты:

# For digit
>>> '1'.isdigit()
True
>>> '1'.isalpha()
False

Проверьте строку как положительную / отрицательную - integer / float

str.isdigit() возвращает False если строка отрицательный номер или номер поплавка. Например:

# returns `False` for float
>>> '123.3'.isdigit()
False
# returns `False` for negative number
>>> '-123'.isdigit()
False

если вы хотите также проверить отрицательный целые числа и float, тогда вы можете написать пользовательскую функцию, чтобы проверить это так:

def is_number(n):
    try:
        float(n)   # Type-casting the string to `float`.
                   # If string is not a valid `float`, 
                   # it'll raise `ValueError` exception
    except ValueError:
        return False
    return True

Пример Запуска:

>>> is_number('123')    # positive integer number
True

>>> is_number('123.4')  # positive float number
True

>>> is_number('-123')   # negative integer number
True

>>> is_number('-123.4') # negative `float` number
True

>>> is_number('abc')    # `False` for "some random" string
False

отбросить строки" NaN " (не число) при проверке на номер

вышеуказанные функции возвратят True для строки" NAN " (не число), потому что для Python допустим float, представляющий его не число. Например:

>>> is_number('NaN')
True

чтобы проверить, является ли число "NaN", вы можете использовать math.isnan() as:

>>> import math
>>> nan_num = float('nan')

>>> math.isnan(nan_num)
True

или если вы не хотите импортировать дополнительную библиотеку, чтобы проверить это, то вы можете просто проверить его, сравнивая его с собой, используя ==. Возвращает на Python False, когда nan float сравнивается с самим собой. Например:

# `nan_num` variable is taken from above example
>>> nan_num == nan_num
False

следовательно, выше функции is_number можно обновить, чтобы вернуть False на "NaN" as:

def is_number(n):
    is_number = True
    try:
        num = float(n)
        # check for "nan" floats
        is_number = num == num   # or use `math.isnan(num)`
    except ValueError:
        is_number = False
    return is_number

образец Беги:

>>> is_number('Nan')   # not a number "Nan" string
False

>>> is_number('nan')   # not a number string "nan" with all lower cased
False

>>> is_number('123')   # positive integer
True

>>> is_number('-123')  # negative integer
True

>>> is_number('-1.12') # negative `float`
True

>>> is_number('abc')   # "some random" string
False

PS: каждая операция для каждой проверки в зависимости от типа номера поставляется с дополнительными накладными расходами. Выберите версию is_number функция которая приспосабливает ваше требование.


Итак, чтобы собрать все это вместе, проверяя Nan, бесконечность и комплексные числа (казалось бы, они указаны с j, а не i, т. е. 1+2j), это приводит к:

def is_number(s):
    try:
        n=str(float(s))
        if n == "nan" or n=="inf" or n=="-inf" : return False
    except ValueError:
        try:
            complex(s) # for complex
        except ValueError:
            return False
    return True

ваш код выглядит нормально для меня.

возможно, вы думаете, что код "неуклюжий" из-за использования исключений? Обратите внимание, что программисты Python склонны использовать исключения, когда это улучшает читаемость кода, благодаря его низкой производительности.


Я сделал тест скорости. Допустим, что если строка скорее чтобы быть номером попробовать/за исключением стратегия является самым быстрым возможным.Если строка не может ряд и вы заинтересованы в целое проверьте, это worths, чтобы сделать некоторый тест (isdigit плюс заголовок '-'). Если вам интересно проверить номер float, вы должны использовать попробовать/за исключением код whitout побег.


мне нужно было определить, входит ли строка в базовые типы (float,int,str, bool). Не найдя ничего в Интернете, я создал это:

def str_to_type (s):
    """ Get possible cast type for a string

    Parameters
    ----------
    s : string

    Returns
    -------
    float,int,str,bool : type
        Depending on what it can be cast to

    """    
    try:                
        f = float(s)        
        if "." not in s:
            return int
        return float
    except ValueError:
        value = s.upper()
        if value == "TRUE" or value == "FALSE":
            return bool
        return type(s)

пример

str_to_type("true") # bool
str_to_type("6.0") # float
str_to_type("6") # int
str_to_type("6abc") # str
str_to_type(u"6abc") # unicode       

вы можете захватить тип и использовать его

s = "6.0"
type_ = str_to_type(s) # float
f = type_(s) 

RyanN предполагает

Если вы хотите вернуть False для NaN и Inf, измените строку на x = float(s); return (x == x) и (x - 1 != икс.) Это должно вернуть True для всех поплавков, кроме Inf и NaN

но это не совсем работа, потому что для достаточно больших поплавков, x-1 == x возвращает true. Например, 2.0**54 - 1 == 2.0**54


входные данные могут быть следующими:

a="50" b=50 c=50.1 d="50.1"


1-Общий вход:

вход этой функции может быть всем!

определяет, является ли данная переменная числовой. Числовые строки состоят из необязательного знака, любого количества цифр, необязательной десятичной части и необязательной экспоненциальной части. Таким образом +0123.45e6-допустимое числовое значение. Шестнадцатеричный (например, 0xf4c3b00c) и двоичный (например 0b10100111001) нотация не допускается.

is_numeric функции

import ast
import number
def is_numeric(obj):
    if isinstance(obj, numbers.Number):
        return True
    elif isinstance(obj, str):
        nodes = list(ast.walk(ast.parse(obj)))[1:]
        if not isinstance(nodes[0], ast.Expr):
            return False
        if not isinstance(nodes[-1], ast.Num):
            return False
        nodes = nodes[1:-1]
        for i in range(len(nodes)):
            #if used + or - in digit :
            if i % 2 == 0:
                if not isinstance(nodes[i], ast.UnaryOp):
                    return False
            else:
                if not isinstance(nodes[i], (ast.USub, ast.UAdd)):
                    return False
        return True
    else:
        return False
.isdigit() метод
>>> a=454
>>> a.isdigit()
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
AttributeError: 'int' object has no attribute 'isdigit'
>>> a="454"
>>> a.isdigit()
True

3-численный входной сигнал:

обнаружение значения int:

>>> isinstance("54", int)
False
>>> isinstance(54, int)
True
>>> 

обнаружение float:

>>> isinstance("45.1", float)
False
>>> isinstance(45.1, float)
True

Я также использовал функцию, которую вы упомянули, но вскоре я заметил, что строки как "Nan", "Inf" и его вариация считаются числом. Поэтому я предлагаю вам улучшенную версию вашей функции, которая вернет false на эти типы ввода и не подведет варианты "1e3":

def is_float(text):
    try:
        float(text)
        # check for nan/infinity etc.
        if text.isalpha():
            return False
        return True
    except ValueError:
        return False

Если вы хотите знать, если всего string можно представить как число, которое вы хотите использовать регулярное выражение (или, возможно, преобразовать float обратно в строку и сравнить ее с исходной строкой, но я предполагаю, что это не очень быстро).


вот мой простой способ сделать это. Предположим, я перебираю некоторые строки и хочу добавить их в массив, если они окажутся числами.

try:
    myvar.append( float(string_to_check) )
except:
    continue

замените myvar.apppend с любой операцией, которую вы хотите сделать со строкой, если она окажется числом. Идея состоит в том, чтобы попытаться использовать операцию float() и использовать возвращенную ошибку, чтобы определить, является ли строка числом.


вы можете обобщить метод исключения полезным способом, возвращая больше полезных значений, чем True и False. Например, эта функция ставит кавычки вокруг строк, но оставляет числа в покое. Это именно то, что мне нужно для быстрого и грязного фильтра, чтобы сделать некоторые определения переменных для R.

import sys

def fix_quotes(s):
    try:
        float(s)
        return s
    except ValueError:
        return '"{0}"'.format(s)

for line in sys.stdin:
    input = line.split()
    print input[0], '<- c(', ','.join(fix_quotes(c) for c in input[1:]), ')'

Я работал над проблемой, которая привела меня к этой теме, а именно, как наиболее интуитивно преобразовать коллекцию данных в строки и числа. Прочитав исходный код, я понял, что то, что мне нужно, отличается двумя способами:

1-Мне нужен целочисленный результат, если строка представляет целое число

2-я хотел, чтобы число или результат строки вставлялись в структуру данных

поэтому я адаптировал исходный код для создания этого производное:

def string_or_number(s):
    try:
        z = int(s)
        return z
    except ValueError:
        try:
            z = float(s)
            return z
        except ValueError:
            return s

попробуйте это.

 def is_number(var):
    try:
       if var == int(var):
            return True
    except Exception:
        return False

import re
def is_number(num):
    pattern = re.compile(r'^[-+]?[-0-9]\d*\.\d*|[-+]?\.?[0-9]\d*$')
    result = pattern.match(num)
    if result:
        return True
    else:
        return False


​>>>: is_number('1')
True

>>>: is_number('111')
True

>>>: is_number('11.1')
True

>>>: is_number('-11.1')
True

>>>: is_number('inf')
False

>>>: is_number('-inf')
False

используйте следующий он обрабатывает все случаи: -

import re
a=re.match('((\d+[\.]\d*$)|(\.)\d+$)' ,  '2.3') 
a=re.match('((\d+[\.]\d*$)|(\.)\d+$)' ,  '2.')
a=re.match('((\d+[\.]\d*$)|(\.)\d+$)' ,  '.3')
a=re.match('((\d+[\.]\d*$)|(\.)\d+$)' ,  '2.3sd')
a=re.match('((\d+[\.]\d*$)|(\.)\d+$)' ,  '2.3')