Як витягнути числа з рядка в Python?


432

Я отримав би всі числа, що містяться в рядку. Що найкраще підходить для мети, регулярних виразів абоisdigit() методу?

Приклад:

line = "hello 12 hi 89"

Результат:

[12, 89]

Відповіді:


485

Якщо ви бажаєте витягнути лише додатні цілі числа, спробуйте наступне:

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

Це не розпізнає плавці, від’ємні цілі числа чи цілі числа у шістнадцятковому форматі. Якщо ви не можете прийняти ці обмеження, тонка відповідь подана нижче, зробить свою справу.


5
це не вдасться для випадку типу "h3110 23 кішка 444,4 кролик 11-2 собака"
sharafjaffri

8
Нормативна справа використовується re. Це загальний і потужний інструмент (тому ви дізнаєтесь щось дуже корисне). Швидкість дещо не має значення при аналізі журналу (зрештою, це не якийсь інтенсивний чисельний вирішувач), reмодуль знаходиться у стандартній бібліотеці Python, і завантажувати його не завадить.
Іоанніс Філіппідіс

19
У мене були такі струни, mumblejumble45mumblejumbleв яких я знав, що є лише одне число. Рішення просто int(filter(str.isdigit, your_string)).
Jonas Lindeløv

1
Невеликий коментар: ви визначаєте змінну, strяка потім переосмислює strоб'єкт і метод в базовому пітоні. Це не є хорошою практикою, оскільки вам це може знадобитися пізніше в сценарії.
Jonas Lindeløv

11
int(filter(...))підніме TypeError: int() argument must be a string...для Python 3.5, тому ви можете використовувати оновлену версію: int(''.join(filter(str.isdigit, your_string)))для вилучення всіх цифр до одного цілого числа.
Марк Мішин

449

Я б використовував регулярний вираз:

>>> 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]

9
... а потім нанесіть intна нього карту, і ви закінчите. +1 особливо для останньої частини. Я б хотів запропонувати сирі рядки ( r'\b\d+\b' == '\\b\\d+\\b').

5
Він може бути int_list = [int(s) for s in re.findall('\\d+', 'hello 12 hi 89')]
внесений

7
@GreenMatt: це технічно розуміння списку (а не генератор), але я погоджуюся, що розуміння / генератори більше пітонічні, ніж map.
Сет Джонсон

1
@Seth Johnson: На жаль! Ти маєш рацію, я неправильно вписав те, що було, як видно, затуманено. :-( Дякую за виправлення!
GreenMatt

2
Однак у мене є проблема. Що робити, якщо я хочу витягнути числа з плаваючою величиною також на зразок 1,45 у "привіт1,45 привіт". Це дасть мені 1 і 45, як два різні числа
ab123

89

Це більш ніж пізно, але ви можете розширити вираз регулярного вираження, щоб врахувати і наукові позначення.

import re

# Format is [(<string>, <expected output>), ...]
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)

Все добре дає!

Крім того, ви можете подивитися вбудований регулярний вираз AWS Glue


1
Оскільки це єдина відповідь, яка комусь подобається, ось як це зробити за допомогою наукової нотації "[- +]? \ D + [\.]? \ D * [Ee]? \ D *". Або якась варіація. Веселіться!
aidan.plenert.macdonald

Виявіть, що існує проблема з найпростішим випадком, наприклад, s = "4"повернення не відповідає. Чи можна редагувати, щоб також про це піклуватися?
batFINGER

1
приємно, але це не обробляє коми (наприклад, 74 600)
yekta

Більш багатослівна група: [+-]?\d*[\.]?\d*(?:(?:[eE])[+-]?\d+)?Ця група дає деякі помилкові позитиви (тобто +захоплюється сама часом), але здатна обробляти більше форм, наприклад .001, плюс вона не поєднує числа автоматично (як у s=2+1)
DavisDude,

24
Ага так, очевидний [-+]?[.]?[\d]+(?:,\d\d\d)*[\.]?\d*(?:[eE][-+]?\d+)?- такий дурний мене ... як я не міг про це думати?
Przemek D

70

Я припускаю, що ви хочете, щоб плаває не просто цілі числа, тому я б зробив щось подібне:

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.
Гюго

3
Для від’ємних чисел:re.findall("[-\d]+", "1 -2")
ytpillai

Чи є якась різниця, якщо ми пишемо continueзамість passцього циклу?
Д. Джонс

Це сприймає більше ніж просто додатні цілі числа, але, використовуючи split (), буде пропущено цифри, що мають символи валюти, що передують першій цифрі, без пробілу, що є звичайним у фінансових документах
Marc Maxmeister

Не працює для поплавків, які не мають місця з іншими символами, наприклад: "4,5 к. Речі" буде працювати, "4,5 к. Штук" не буде.
Джей Д.

64

Якщо ви знаєте, що це буде лише одне число в рядку, тобто "привіт 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

12
У 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')))
Кент Мунт Касперсен

16
# 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]

3
Ласкаво просимо до ТА та дякуємо за публікацію відповіді. Завжди добре застосовувати додаткові коментарі до своєї відповіді і чому це вирішує проблему, а не просто розміщувати фрагмент коду.
sebs

не працював у моєму випадку. мало чим відрізняється від відповіді вище
oldboy

ValueError: не вдалося перетворити рядок у float: 'e', ​​і він не працює в деяких випадках :(
Vilq

15

Я шукав рішення, як зняти маски рядків, зокрема з номерів бразильських телефонів, ця публікація не відповіла, але мене надихнула. Це моє рішення:

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

12

Використання Regex нижче - це спосіб

lines = "hello 12 hi 89"
import re
output = []
#repl_str = re.compile('\d+.?\d*')
repl_str = re.compile('^\d+$')
#t = r'\d+.?\d*'
line = lines.split()
for word in line:
        match = re.search(repl_str, word)
        if match:
            output.append(float(match.group()))
print (output)

з Findall re.findall(r'\d+', "hello 12 hi 89")

['12', '89']

re.findall(r'\b\d+\b', "hello 12 hi 89 33F AC 777")

 ['12', '89', '777']

Вам слід принаймні зібрати регулярний вираз, якщо ви не використовуєтеfindall()
information_interchange

2
repl_str = re.compile('\d+.?\d*') має бути: repl_str = re.compile('\d+\.?\d*') Для відтворюваного прикладу з використанням python3.7 re.search(re.compile(r'\d+.?\d*'), "42G").group() '42G' re.search(re.compile(r'\d+\.?\d*'), "42G").group() '42'
Alexis Lucattini

8
line2 = "hello 12 hi 89"
temp1 = re.findall(r'\d+', line2) # through regular expression
res2 = list(map(int, temp1))
print(res2)

Привіт ,

ви можете шукати всі цілі числа в рядку через цифру, використовуючи вираз findall.

На другому кроці створіть список res2 та додайте до цього списку цифри, знайдені в рядку

сподіваюся, що це допомагає

З повагою, Дівакар Шарма


Надану відповідь було позначено для ознайомлення як допис низької якості. Ось декілька вказівок щодо того, як написати гарну відповідь? . Ця надана відповідь може бути правильною, але вона може отримати користь від пояснення. Відповіді лише з коду не вважаються "хорошими" відповідями. З огляду .
Trenton McKinney

просте та робоче рішення, високо оцінено
moyo

7

Ця відповідь також містить випадок, коли число є плаваючою в рядку

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)

5

Я вражений тим, що ніхто ще не згадав про використання 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буде:

[12, 89]

PS: Це просто задля ілюстрації, щоб показати, що як альтернативу ми також можемо використовуватись groupbyдля досягнення цього. Але це не рекомендується рішення. Якщо ви хочете досягти цього, вам слід використовувати прийняту відповідь fmark, засновану на розумінні списку з str.isdigitфільтром.


4

Я просто додаю цю відповідь, тому що ніхто не додав жодної, використовуючи обробку Exception, і тому що це також працює для плаваючих елементів

a = []
line = "abcd 1234 efgh 56.78 ij"
for word in line.split():
    try:
        a.append(float(word))
    except ValueError:
        pass
print(a)

Вихід:

[1234.0, 56.78]

3

Для лову різних моделей корисно проводити запит з різними шаблонами.

Налаштуйте всі шаблони, що охоплюють різні цікаві моделі:

(знаходить коми) 12 300 або 12 300,00

'[\ d] + [., \ d] +'

(знаходить поплавці) 0,123 або .123

'[\ d] * [.] [\ d] +'

(знаходить цілі числа) 123

'[\ d] +'

Комбінуйте з трубою (|) в одну схему з декількома або умовними .

(Примітка. Поставте спочатку складні візерунки. Ще прості візерунки повернуть шматочки складного улову замість складного вилову, який повертає повний вилов).

p = '[\d]+[.,\d]+|[\d]*[.][\d]+|[\d]+'

Нижче ми підтвердимо, що шаблон присутній re.search(), а потім повернемо ітерабельний список уловів. Нарешті, ми надрукуємо кожен улов, використовуючи позначення дужок, щоб підсележувати значення об'єкта відповідності від об'єкта відповідності.

s = 'he33llo 42 I\'m a 32 string 30 444.4 12,001'

if re.search(p, s) is not None:
    for catch in re.finditer(p, s):
        print(catch[0]) # catch is a match object

Повернення:

33
42
32
30
444.4
12,001

2

Оскільки жоден із них не стосувався реальних фінансових цифр у програмах excel та word, які мені потрібно було знайти, ось моя зміна. Він обробляє вставки, поплавці, негативні числа, валютні номери (оскільки він не відповідає розділенням) і має можливість скинути десяткову частину і просто повернути вставки або повернути все.

Він також обробляє систему числення Indian Laks, де коми з’являються нерегулярно, а не кожні 3 числа.

Це не обробляє наукові позначення або негативні цифри, що містяться в дужках у бюджетах - виявляться позитивними.

Він також не витягує дати. Є кращі способи пошуку дат у рядках.

import re
def find_numbers(string, ints=True):            
    numexp = re.compile(r'[-]?\d[\d,]*[\.]?[\d{2}]*') #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

1

@jmnas, мені сподобалась ваша відповідь, але вона не знайшла поплавців. Я працюю над сценарієм для розбору коду, що надходить до фрези з ЧПУ, і мені потрібно було знайти розміри X і Y, які можуть бути цілими чи плаваючими, тому я адаптував ваш код до наступного. Це знаходить int, float з позитивними та негативними vals. Досі не знаходять значень у форматі шістнадцяткових значень, але ви можете додати "x" та "A" через "F" до num_charкортежу, і я думаю, що це би розібрало такі речі, як "0x23AC".

s = 'hello X42 I\'m a Y-32.35 string Z30'
xy = ("X", "Y")
num_char = (".", "+", "-")

l = []

tokens = s.split()
for token in tokens:

    if token.startswith(xy):
        num = ""
        for char in token:
            # print(char)
            if char.isdigit() or (char in num_char):
                num = num + char

        try:
            l.append(float(num))
        except ValueError:
            pass

print(l)

0

Найкращий варіант, який я знайшов, наведений нижче. Це витягне число і може усунути будь-який тип знаків.

def extract_nbr(input_str):
    if input_str is None or input_str == '':
        return 0

    out_number = ''
    for ele in input_str:
        if ele.isdigit():
            out_number += ele
    return float(out_number)    

0

Для телефонних номерів можна просто виключити всі нецифрові символи з \ D у регулярному виразі:

import re

phone_number = '(619) 459-3635'
phone_number = re.sub(r"\D", "", phone_number)
print(phone_number)
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.