Python выбрать только числа

Python: извлечение чисел из строки

Я извлечу все числа, содержащиеся в строке. Что лучше подходит для этой цели, регулярные выражения или метод isdigit() ? Пример:

13 ответов

Если вы хотите извлечь только целые положительные числа, попробуйте следующее:

>>> str = "h3110 23 cat 444.4 rabbit 11 2 dog" >>> [int(s) for s in str.split() if s.isdigit()] [23, 11, 2] 

Я бы сказал, что это лучше, чем пример регулярного выражения по трем причинам. Во-первых, вам не нужен другой модуль; во-вторых, это более читаемо, потому что вам не нужно разбирать мини-язык регулярных выражений; и, в-третьих, он быстрее (и, следовательно, скорее, более питонический):

python -m timeit -s "str = 'h3110 23 cat 444.4 rabbit 11 2 dog' * 1000" "[s for s in str.split() if s.isdigit()]" 100 loops, best of 3: 2.84 msec per loop python -m timeit -s "import re" "str = 'h3110 23 cat 444.4 rabbit 11 2 dog' * 1000" "re.findall('\\b\\d+\\b', str)" 100 loops, best of 3: 5.66 msec per loop 

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

@ Крис Морган Правда. Не сравнение яблок с яблоками, хотя с регулярным выражением. Я изменил ответ, но не время.

Нормативный случай использует re . Это общий и мощный инструмент (поэтому вы узнаете что-то очень полезное). Скорость несколько не имеет значения при разборе журнала (это не какой-то интенсивный числовой решатель в конце концов), модуль re находится в стандартной библиотеке Python, и его загрузка не помешает.

Замените int(s) на int(s.replace(‘,’, »)) и s.isdigit() на s[0].isdigit() и он будет обрабатывать числа, даже если у них запятая в виде тысяч разделитель в нем. У этого недостатка есть недостаток, что он не работает на 444.4 в вашем примере, но если вы пытаетесь обработать такой сложный ввод, возможно, вам лучше использовать выделенную функцию, а не однострочный.

У меня были строки вроде mumblejumble45mumblejumble в которых я знал, что был только один номер. Решение — просто int(filter(str.isdigit, your_string)) .

Небольшой комментарий: вы определяете переменную str которая затем переопределяет объект и метод str в базовом python. Это не очень хорошая практика, так как она может понадобиться вам позже в сценарии.

Оба python2 (2.7.10) и python3 (3.4.2) почти на два порядка быстрее , здесь делают на re версию. С какой версией python вы делали эти тесты ?!

@Jonas, что ты имел в виду под «я знал, что был только один номер»? Предлагаемое вами решение также работает для ситуации с более чем одним числом в строке, например, ‘plant_16_day_9_hour_9_label_fmp.png’ , дает: 1699. или я мог что-то упустить?

@ Gathide, если это твое желаемое поведение, то идеально. Но это не обязательно, и это не будет вызывать предупреждение. Я вижу, что dfostic позже опубликовал полный ответ с этой стратегией, также подчеркнув это поведение предупреждением.

Читайте также:  Удалить все версии java linux

int(filter(. )) вызовет TypeError: int() argument must be a string. для Python 3.5, поэтому вы можете использовать обновленную версию: int(».join(filter(str.isdigit, your_string))) для извлечения всех цифр в одно целое число.

не работает: « `>>> str = ‘issue_date [200]’ >>> [int (s) для s в str.split (), если s.isdigit ()] []` «

Я запускаю ваш тест и python -m timeit -s «str = ‘h3110 23 cat 444.4 rabbit 11 2 dog’ * 1000» «[s for s in str.split() if s.isdigit()]» 1000 loops, best of 3: 595 usec per loop совершенно другой результат: python -m timeit -s «str = ‘h3110 23 cat 444.4 rabbit 11 2 dog’ * 1000» «[s for s in str.split() if s.isdigit()]» 1000 loops, best of 3: 595 usec per loop и python -m timeit -s «import re» «str = ‘h3110 23 cat 444.4 rabbit 11 2 dog’ * 1000» «re.findall(‘\\b\\d+\\b’, str)» 100000 loops, best of 3: 12 usec per loop

Это хорошо, но для случаев, когда число НЕ окружено пробелами, решение (возможно, с использованием filter(str.isdigit, string) ) становится слишком сложным. Самый гладкий путь на сегодняшний день был бы регулярным выражением. Regex должен быть частью арсенала каждого разработчика. Они пугающие, но их легко понять; избегать их слишком контрпродуктивно. И путь Pythonic не был бы самым быстрым, он был бы самым чистым и самым понятным.

Вариант Джонаса Линделова в комментариях выше, чтобы получить только первое firstNumberWord = next(filter(str.isdigit, myString.split())) слово: firstNumberWord = next(filter(str.isdigit, myString.split())) (В моем случае это была строка, подобная предложению, поэтому .split() разделяет его на слова)

>>> import re >>> re.findall(r'\d+', 'hello 42 I\'m a 32 string 30') ['42', '32', '30'] 

Это также соответствует 42 из bla42bla . Если вам нужны только числа, ограниченные границами слов (пробел, период, запятая), вы можете использовать \b:

>>> re.findall(r'\b\d+\b', 'he33llo 42 I\'m a 32 string 30') ['42', '32', '30'] 

В итоге получится список чисел вместо списка строк:

>>> [int(s) for s in re.findall(r'\b\d+\b', 'he33llo 42 I\'m a 32 string 30')] [42, 32, 30] 

. а затем int на карту int и все готово. +1 особенно для последней части. Я бы предложил исходные строки ( r’\b\d+\b’ == ‘\\b\\d+\\b’ ).

Его можно поместить в список с генератором, например: int_list = [int(s) for s in re.findall(‘\\d+’, ‘hello 12 hi 89’)]

@GreenMatt: технически это понимание списка (а не генератор), но я бы согласился, что понимания / генераторы более Pythonic, чем map .

@ Сид Джонсон: Ой! Вы правы, я опечатка в том, что было, по-видимому, в туманном состоянии ума. 🙁 Спасибо за исправление!

@delnan — Если у вас есть подобное предложение, отредактируйте его в ответ . не оставляйте его как комментарий, который люди могут или не могут заметить.

Или используйте map для преобразования строк в целые числа, как в map(int, re.findall(r’\b\d+\b’, ‘he33llo 42 I\’ma 32 string 30’))

Читайте также:  ' . wp_get_document_title() . '

У меня проблема, хотя. Что делать, если я хочу извлечь числа с плавающей точкой, такие как 1.45 в «hello1.45 hi». Это даст мне 1 и 45 как два разных числа

Это более чем поздно, но вы можете расширить выражение регулярного выражения, чтобы учесть и научную нотацию.

import re # Format is [(, ), . ] ss = [("apple-12.34 ba33na fanc-14.23e-2yapple+45e5+67.56E+3", ['-12.34', '33', '-14.23e-2', '+45e5', '+67.56E+3']), ('hello X42 I\'m a Y-32.35 string Z30', ['42', '-32.35', '30']), ('he33llo 42 I\'m a 32 string -30', ['33', '42', '32', '-30']), ('h3110 23 cat 444.4 rabbit 11 2 dog', ['3110', '23', '444.4', '11', '2']), ('hello 12 hi 89', ['12', '89']), ('4', ['4']), ('I like 74,600 commas not,500', ['74,600', '500']), ('I like bad math 1+2=.001', ['1', '+2', '.001'])] for s, r in ss: rr = re.findall("[-+]?[.]?[\d]+(. \d\d\d)*[\.]?\d*(?:[eE][-+]?\d+)?", s) if rr == r: print('GOOD') else: print('WRONG', rr, 'should be', r) 

Поскольку это единственный ответ, который кому-либо нравится, вот как это сделать с помощью научной нотации «[- +]? \ D + [\.]? \ D * [Ee]? \ D *». Или какой-то вариант. Повеселись!

Найти есть проблема с простейшим случаем, например, s = «4» возвращает совпадений. Можно ли отредактировать, чтобы позаботиться об этом?

Более подробной группой является [+-]?\d*[\.]?\d*(?:(?:[eE])[+-]?\d+)? Эта группа дает некоторые ложные срабатывания (то есть + иногда фиксируется сама собой), но может обрабатывать больше форм, например .001 , плюс она не объединяет числа автоматически (как в s=2+1 )

@DavisDude Вроде исправили это для вас. «1 + 2» -> [‘1’, ‘+2’], но этого должно быть достаточно для большинства применений

Ах да, очевидное [-+]?[.]?[\d]+(. \d\d\d)*[\.]?\d*(?:[eE][-+]?\d+)? — так глупо с моей стороны . как я мог не думать об этом?

Я предполагаю, что вы хотите плавать не только целыми числами, поэтому я бы сделал что-то вроде этого:

l = [] for t in s.split(): try: l.append(float(t)) except ValueError: pass 

Обратите внимание, что некоторые другие решения, размещенные здесь, не работают с отрицательными номерами:

>>> re.findall(r'\b\d+\b', 'he33llo 42 I\'m a 32 string -30') ['42', '32', '30'] >>> '-3'.isdigit() False 

Это находит положительные и отрицательные числа с плавающей запятой и целые числа. Для только положительных и отрицательных целых чисел измените float на int .

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

Не работает для чисел с плавающей запятой, в которых нет пробелов с другими символами, например: «4.5 k вещи» будут работать, «4.5k вещи» не будет.

Если вы знаете, что в строке будет только одно число, то есть «привет 12 привет», вы можете попробовать фильтр.

In [1]: int(''.join(filter(str.isdigit, '200 grams'))) Out[1]: 200 In [2]: int(''.join(filter(str.isdigit, 'Counters: 55'))) Out[2]: 55 In [3]: int(''.join(filter(str.isdigit, 'more than 23 times'))) Out[3]: 23 
In [4]: int(''.join(filter(str.isdigit, '200 grams 5'))) Out[4]: 2005 

В Python 3.6.3 я получил TypeError: int() argument must be a string, a bytes-like object or a number, not ‘filter’ — исправляя его с помощью int(«».join(filter(str.isdigit, ‘200 grams’)))

# extract numbers from garbage string: s = '12//n,_@#$%3.14kjlw0xdadfackvj1.6e-19&*ghn334' newstr = ''.join((ch if ch in '0123456789.-e' else ' ') for ch in s) listOfNumbers = [float(i) for i in newstr.split()] print(listOfNumbers) [12.0, 3.14, 0.0, 1.6e-19, 334.0] 

Добро пожаловать в ТАК и спасибо за размещение ответа. Хорошей практикой всегда является добавление некоторых дополнительных комментариев к вашему ответу и почему оно решает проблему, а не просто публикация фрагмента кода.

Читайте также:  Include header file in python

В этом ответе также содержится случай, когда число с плавающей точкой в ​​строке

def get_first_nbr_from_str(input_str): ''' :param input_str: strings that contains digit and words :return: the number extracted from the input_str demo: 'ab324.23.123xyz': 324.23 '.5abc44': 0.5 ''' if not input_str and not isinstance(input_str, str): return 0 out_number = '' for ele in input_str: if (ele == '.' and '.' not in out_number) or ele.isdigit(): out_number += ele elif out_number: break return float(out_number) 

Я искал решение для удаления строковых масок, особенно с бразильских телефонных номеров, этот пост не ответил, но вдохновил меня. Это мое решение:

>>> phone_number = '+55(11)8715-9877' >>> ''.join([n for n in phone_number if n.isdigit()]) '551187159877' 

Я поражен, увидев, что никто еще не упомянул использование itertools.groupby в качестве альтернативы для достижения этой цели.

Вы можете использовать itertools.groupby() вместе с str.isdigit() для извлечения чисел из строки как:

from itertools import groupby my_str = "hello 12 hi 89" l = [int(''.join(i)) for is_digit, i in groupby(my_str, str.isdigit) if is_digit] 

Значение, удерживаемое l будет:

PS: Это просто для иллюстрации, чтобы показать, что в качестве альтернативы мы могли бы также использовать groupby для достижения этой цели. Но это не рекомендуемое решение. Если вы хотите добиться этого, вы должны использовать принятый ответ fmark, основанный на использовании понимания списка с str.isdigit качестве фильтра.

Поскольку ни один из них не занимался финансовыми цифрами реального мира в документах Excel и Word, которые мне нужно было найти, вот моя вариация. Он обрабатывает ints, float, отрицательные числа, номера валют (потому что он не отвечает на split), и имеет возможность отбрасывать десятичную часть и просто возвращать ints или возвращать все.

Он также обрабатывает систему чисел индийских лаков, где запятые появляются нерегулярно, а не каждые 3 числа друг от друга.

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

Он также не выделяет даты. Есть лучшие способы поиска дат в строках.

import re def find_numbers(string, ints=True): numexp = re.compile(r'[-]?\d[\d,]*[\.]?[\d]*') #optional - in front numbers = numexp.findall(string) numbers = [x.replace(',','') for x in numbers] if ints is True: return [int(x.replace(',','').split('.')[0]) for x in numbers] else: return numbers 

Источник

Оцените статью