Как проверить, является ли строка числом (с плавающей запятой)?
Каков наилучший способ проверить, может ли строка представляться как число в Python?
У меня сейчас есть функция:
def is_number(s):
try:
float(s)
return True
except ValueError:
return False
Который, не только уродливый и медленный, кажется неуклюжим. Однако я не нашел лучшего метода, потому что вызов float
в основной функции еще хуже.
46 ответов
Что не только уродливо и медленно
Я бы оспорил оба.
Регулярное выражение или другой синтаксический анализ строки были бы более уродливыми и медленными.
Я не уверен, что все может быть быстрее, чем выше. Вызывает функцию и возвращает. Try/Catch не приводит к большим накладным расходам, потому что наиболее распространенное исключение перехватывается без расширенного поиска кадров стека.
Проблема в том, что любая функция преобразования чисел имеет два вида результатов
- Номер, если номер действителен
- Код состояния (например, через errno) или исключение, показывающее, что не может быть проанализировано действительное число.
С (в качестве примера) взломать это можно несколькими способами. Python излагает это ясно и явно.
Я думаю, что ваш код для этого идеально подходит.
Если вы ищете синтаксические (положительные, беззнаковые) целые числа вместо чисел с плавающей запятой, вы можете использовать isdigit()
функция для строковых объектов.
>>> a = "03523"
>>> a.isdigit()
True
>>> b = "963spam"
>>> b.isdigit()
False
Строковые методы - isdigit()
Есть также кое-что о строках Unicode, с которыми я не слишком знаком с Unicode - является десятичным / десятичным
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()
Если строка не является числом, блок исключений работает довольно медленно. Но что еще более важно, метод try-исключения является единственным подходом, который правильно обрабатывает научные записи.
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__)
Нотация с плавающей точкой ".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.000000e+50" не поддерживается:
- is_number_regex
- is_number_repl_isdigit
Научная нотация "1е50" не поддерживается:
- is_number_regex
- is_number_repl_isdigit
РЕДАКТИРОВАТЬ: результаты тестов
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()
Есть одно исключение, которое вы можете принять во внимание: строка "NaN"
Если вы хотите, чтобы is_number возвращал FALSE для 'NaN', этот код не будет работать, поскольку Python преобразует его в представление числа, которое не является числом (поговорим о проблемах идентификации):
>>> float('NaN')
nan
В противном случае мне следует поблагодарить вас за фрагмент кода, который я сейчас активно использую.:)
Г.
Как насчет этого:
'3.14'.replace('.','',1).isdigit()
который вернет true, только если есть один или нет '.' в строке цифр.
'3.14.5'.replace('.','',1).isdigit()
вернет ложь
редактировать: только что увидел еще один комментарий... добавление .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
Который, не только уродливый и медленный, кажется неуклюжим.
Это может занять некоторое привыкание, но это питонский способ сделать это. Как уже указывалось, альтернативы хуже. Но есть еще одно преимущество таких вещей: полиморфизм.
Основная идея печатания утки заключается в том, что "если она ходит и говорит как утка, то это утка". Что, если вы решите, что вам нужно разбить строку на подклассы, чтобы вы могли изменить способ определения, может ли что-то быть преобразовано в число с плавающей точкой? Или что, если вы решите проверить какой-то другой объект целиком? Вы можете делать эти вещи без необходимости изменения вышеуказанного кода.
Другие языки решают эти проблемы с помощью интерфейсов. Я сохраню анализ того, какое решение лучше для другого потока. Суть, однако, в том, что python определенно относится к уравнению типа "утка", и вам, вероятно, придется привыкнуть к такому синтаксису, если вы планируете много программировать на Python (но это не означает, что тебе это должно нравиться конечно).
Вы можете принять во внимание еще одну вещь: Python довольно быстро генерирует и перехватывает исключения по сравнению со многими другими языками (например, в 30 раз быстрее, чем.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
Этот ответ предоставляет пошаговое руководство, имеющее функцию с примерами для поиска строки:
- Положительное число
- Положительный / отрицательный - целое число / число с плавающей запятой
- Как отбросить строки "NaN" (не число) при проверке числа?
Проверьте, является ли строка положительным целым числом
Вы можете использовать str.isdigit()
проверить, является ли данная строка положительным целым числом.
Пример результатов:
# For digit
>>> '1'.isdigit()
True
>>> '1'.isalpha()
False
Проверить строку как положительную / отрицательную - целое число / число с плавающей запятой
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 это допустимое число с плавающей точкой, представляющее это не число. Например:
>>> is_number('NaN')
True
Чтобы проверить, является ли номер "NaN", вы можете использовать math.isnan()
как:
>>> import math
>>> nan_num = float('nan')
>>> math.isnan(nan_num)
True
Или, если вы не хотите импортировать дополнительную библиотеку, чтобы проверить это, то вы можете просто проверить ее, сравнив ее с самой собой, используя ==
, Python возвращается False
когда nan
Поплавок сравнивается с самим собой. Например:
# `nan_num` variable is taken from above example
>>> nan_num == nan_num
False
Следовательно, выше функция is_number
можно обновить, чтобы вернуться False
за "NaN"
как:
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
функция, которая соответствует вашим требованиям.
Для строк не чисел, 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
выигрывает в обоих случаях
Просто Mimic C#
В C# есть две разные функции, которые обрабатывают скалярные значения:
- Float.Parse ()
- Float.TryParse ()
float.parse ():
def parse(string):
try:
return float(string)
except Exception:
throw TypeError
Примечание. Если вам интересно, почему я изменил исключение на TypeError, вот документация.
float.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: Я лично предпочитаю называть это Monkey Punching, потому что мне кажется, что я злоупотребляю языком, когда делаю это, но 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
И великий мудрец Питонас сказал Святейшему Престолу Шарпису: "Все, что ты можешь сделать, я могу сделать лучше; я могу сделать что-нибудь лучше тебя".
Я знаю, что это особенно старо, но я хотел бы добавить ответ, который, как я считаю, охватывает информацию, отсутствующую в ответе с наибольшим количеством голосов, который может быть очень ценным для любого, кто найдет это:
Для каждого из следующих методов соедините их со счетчиком, если вам нужно принять какие-либо данные. (Предполагая, что мы используем голосовые определения целых чисел, а не 0-255 и т. Д.)
x.isdigit()
хорошо работает для проверки, если х является целым числом.
x.replace('-','').isdigit()
хорошо работает для проверки, если х является отрицательным. (Проверка - в первой позиции)
x.replace('.','').isdigit()
хорошо работает для проверки, если х является десятичным.
x.replace(':','').isdigit()
хорошо работает для проверки, если х является отношением.
x.replace('/','',1).isdigit()
хорошо работает для проверки, если х является дробью.
Приведение к float и перехват ValueError, вероятно, самый быстрый способ, так как float() специально предназначен именно для этого. Все остальное, требующее синтаксического анализа строки (регулярное выражение и т. Д.), Вероятно, будет медленнее из-за того, что оно не настроено для этой операции. Мои $0,02.
Вы можете использовать строки Unicode, у них есть метод, который делает то, что вы хотите:
>>> s = u"345"
>>> s.isnumeric()
True
Или же:
>>> s = "345"
>>> u = unicode(s)
>>> u.isnumeric()
True
В наиболее общем случае для числа с плавающей запятой нужно позаботиться о целых и десятичных числах. Возьмем веревочку
"1.1"
Например.
Я бы попробовал одно из следующего:
1.> isnumeric ()
word = "1.1"
"".join(word.split(".")).isnumeric()
>>> True
2.> isdigit ()
word = "1.1"
"".join(word.split(".")).isdigit()
>>> True
3.> isdecimal ()
word = "1.1"
"".join(word.split(".")).isdecimal()
>>> True
Скорость:
► Все вышеупомянутые методы имеют одинаковую скорость.
%timeit "".join(word.split(".")).isnumeric()
>>> 257 ns ± 12 ns per loop (mean ± std. dev. of 7 runs, 1000000 loops each)
%timeit "".join(word.split(".")).isdigit()
>>> 252 ns ± 11 ns per loop (mean ± std. dev. of 7 runs, 1000000 loops each)
%timeit "".join(word.split(".")).isdecimal()
>>> 244 ns ± 7.17 ns per loop (mean ± std. dev. of 7 runs, 1000000 loops each)
Я хотел посмотреть, какой метод самый быстрый. В целом лучшие и наиболее последовательные результаты были получены check_replace
функция. Самые быстрые результаты дали check_exception
функции, но только в том случае, если исключений не было - это означает, что его код наиболее эффективен, но накладные расходы на создание исключения довольно велики.
Обратите внимание, что проверка на успешное приведение является единственным методом, который является точным, например, это работает с check_exception
но две другие тестовые функции вернут False для допустимого числа с плавающей запятой:
huge_number = float('1e+100')
Вот эталонный код:
import time, re, random, string
ITERATIONS = 10000000
class Timer:
def __enter__(self):
self.start = time.clock()
return self
def __exit__(self, *args):
self.end = time.clock()
self.interval = self.end - self.start
def check_regexp(x):
return re.compile("^\d*\.?\d*$").match(x) is not None
def check_replace(x):
return x.replace('.','',1).isdigit()
def check_exception(s):
try:
float(s)
return True
except ValueError:
return False
to_check = [check_regexp, check_replace, check_exception]
print('preparing data...')
good_numbers = [
str(random.random() / random.random())
for x in range(ITERATIONS)]
bad_numbers = ['.' + x for x in good_numbers]
strings = [
''.join(random.choice(string.ascii_uppercase + string.digits) for _ in range(random.randint(1,10)))
for x in range(ITERATIONS)]
print('running test...')
for func in to_check:
with Timer() as t:
for x in good_numbers:
res = func(x)
print('%s with good floats: %s' % (func.__name__, t.interval))
with Timer() as t:
for x in bad_numbers:
res = func(x)
print('%s with bad floats: %s' % (func.__name__, t.interval))
with Timer() as t:
for x in strings:
res = func(x)
print('%s with strings: %s' % (func.__name__, t.interval))
Вот результаты с Python 2.7.10 на MacBook Pro 13 2017 года:
check_regexp with good floats: 12.688639
check_regexp with bad floats: 11.624862
check_regexp with strings: 11.349414
check_replace with good floats: 4.419841
check_replace with bad floats: 4.294909
check_replace with strings: 4.086358
check_exception with good floats: 3.276668
check_exception with bad floats: 13.843092
check_exception with strings: 15.786169
Вот результаты с Python 3.6.5 на MacBook Pro 13 2017 года:
check_regexp with good floats: 13.472906000000009
check_regexp with bad floats: 12.977665000000016
check_regexp with strings: 12.417542999999995
check_replace with good floats: 6.011045999999993
check_replace with bad floats: 4.849356
check_replace with strings: 4.282754000000011
check_exception with good floats: 6.039081999999979
check_exception with bad floats: 9.322753000000006
check_exception with strings: 9.952595000000002
Вот результаты с PyPy 2.7.13 на MacBook Pro 13 2017 года:
check_regexp with good floats: 2.693217
check_regexp with bad floats: 2.744819
check_regexp with strings: 2.532414
check_replace with good floats: 0.604367
check_replace with bad floats: 0.538169
check_replace with strings: 0.598664
check_exception with good floats: 1.944103
check_exception with bad floats: 2.449182
check_exception with strings: 2.200056
Ввод может быть следующим:
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
тестовое задание:
>>> is_numeric("54")
True
>>> is_numeric("54.545")
True
>>> is_numeric("0x45")
True
функцияis_float
Находит, является ли данная переменная плавающей. Строки float состоят из необязательного знака, любого количества цифр,...
import ast
def is_float(obj):
if isinstance(obj, float):
return True
if isinstance(obj, int):
return False
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
if not isinstance(nodes[-1].n, float):
return False
nodes = nodes[1:-1]
for i in range(len(nodes)):
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
тестовое задание:
>>> is_float("5.4")
True
>>> is_float("5")
False
>>> is_float(5)
False
>>> is_float("5")
False
>>> is_float("+5.4")
True
что такое аст?
2- Если вы уверены, что переменным содержимым является строка:
использовать метод str.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
>>>
обнаружить поплавок:
>>> isinstance("45.1", float)
False
>>> isinstance(45.1, float)
True
Таким образом, чтобы сложить все вместе, проверяя 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
Допустим, у вас есть цифры в строке. str = "100949", и вы хотели бы проверить, есть ли в нем только цифры
if str.isdigit():
returns TRUE or FALSE
в противном случае ваш метод отлично работает, чтобы найти в строке цифру.
Я думаю, что ваше решение в порядке.
Сказав это, есть много ненависти к этим ответам, которые я считаю неоправданными, регулярные выражения могут быть достаточно чистыми, правильными и быстрыми. Это действительно зависит от того, что вы пытаетесь сделать. Первоначальный вопрос заключался в том, как можно "проверить, можно ли представить строку в виде числа (с плавающей запятой)" (согласно вашему названию). Предположительно, вы захотите использовать числовое значение / значение с плавающей запятой после того, как вы проверите, что оно допустимо, и в этом случае ваша попытка / исключение имеет большой смысл. Но если по какой-то причине вы просто хотите проверить, что строка является числом, то регулярное выражение также работает нормально, но его трудно получить правильно. Я думаю, что большинство ответов на регулярные выражения до сих пор, например, неправильно анализируют строки без целочисленной части (такой как ".7"), которая является плавающей точкой в том, что касается python. И это немного сложно проверить в одном регулярном выражении, где дробная часть не требуется. Я включил два регулярных выражения, чтобы показать это.
Это поднимает интересный вопрос о том, что такое "число". Вы включаете "inf", который является допустимым как float в python? Или вы включаете числа, которые являются "числами", но, возможно, не могут быть представлены в Python (например, числа, которые больше, чем максимум с плавающей точкой).
Есть также неясности в том, как вы анализируете числа. Например, как насчет "--20"? Это "число"? Это законный способ представлять "20"? Python позволит вам сделать "var = --20" и установить его в 20 (хотя на самом деле это потому, что он обрабатывает это как выражение), но float("-20") не работает.
В любом случае, без дополнительной информации, вот регулярное выражение, которое, я считаю, покрывает все целые числа и числа с плавающей запятой, когда python анализирует их.
# Doesn't properly handle floats missing the integer part, such as ".7"
SIMPLE_FLOAT_REGEXP = re.compile(r'^[-+]?[0-9]+\.?[0-9]+([eE][-+]?[0-9]+)?$')
# Example "-12.34E+56" # sign (-)
# integer (12)
# mantissa (34)
# exponent (E+56)
# Should handle all floats
FLOAT_REGEXP = re.compile(r'^[-+]?([0-9]+|[0-9]*\.[0-9]+)([eE][-+]?[0-9]+)?$')
# Example "-12.34E+56" # sign (-)
# integer (12)
# OR
# int/mantissa (12.34)
# exponent (E+56)
def is_float(str):
return True if FLOAT_REGEXP.match(str) else False
Некоторые примеры тестовых значений:
True <- +42
True <- +42.42
False <- +42.42.22
True <- +42.42e22
True <- +42.42E-22
False <- +42.42e-22.8
True <- .42
False <- 42nope
Возвращение
True
если все символы в строке числовые, и есть хотя бы один символ,False
в противном случае. Числовые символы включают символы цифр и все символы, которые имеют свойство числового значения Unicode, например U+2155, VULGAR FRACTION ONE FIFTH. Формально числовые символы - это символы со значением свойства Numeric_Type=Digit, Numeric_Type=Decimal или Numeric_Type=Numeric.
Возвращение
True
если все символы в строке являются десятичными и имеется хотя бы один символ,False
в противном случае. Десятичные символы - это символы, которые можно использовать для образования чисел с основанием 10, например, U+0660, АРАБСКИЙ ЦИФРОВОЙ ЦИФР НУЛЬ. Формально десятичный символ - это символ из общей категории Unicode "Nd".
Оба доступны для строковых типов из Python 3.0.
Ваш код выглядит хорошо для меня.
Возможно, вы думаете, что код "неуклюжий" из-за использования исключений? Обратите внимание, что программисты на Python склонны свободно использовать исключения, когда это улучшает читаемость кода, из-за низкого снижения производительности.
Мне нужно было определить, приведена ли строка к базовым типам (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)
Я сделал тест скорости. Допустим, что если строка, вероятно, будет числом, стратегия try / исключением является самой быстрой из возможных. Если строка, скорее всего, не является числом, и вас интересует проверка целых чисел, стоит провести некоторый тест (isdigit плюс заголовок '-'). Если вы заинтересованы в проверке числа с плавающей запятой, вы должны использовать пробный / исключающий код без выхода.
Я работал над проблемой, которая привела меня к этой теме, а именно, как преобразовать коллекцию данных в строки и числа наиболее интуитивным способом. После прочтения исходного кода я понял, что то, что мне было нужно, отличалось двумя способами:
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
Этот код обрабатывает показатели, числа с плавающей точкой и целые числа, без использования регулярных выражений.
return True if str1.lstrip('-').replace('.','',1).isdigit() or float(str1) else False
RyanN предлагает
Если вы хотите вернуть False для NaN и Inf, измените строку на x = float(s); return (x == x) и (x - 1!= x). Это должно вернуть True для всех чисел, кроме Inf и NaN
Но это не совсем работает, потому что для достаточно больших поплавков, x-1 == x
возвращает истину. Например, 2.0**54 - 1 == 2.0**54
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
Я также использовал функцию, которую вы упомянули, но вскоре я заметил, что строки как "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
def is_float(s):
if s is None:
return False
if len(s) == 0:
return False
digits_count = 0
dots_count = 0
signs_count = 0
for c in s:
if '0' <= c <= '9':
digits_count += 1
elif c == '.':
dots_count += 1
elif c == '-' or c == '+':
signs_count += 1
else:
return False
if digits_count == 0:
return False
if dots_count > 1:
return False
if signs_count > 1:
return False
return True