Bagaimana cara mengekstraksi angka dari string dengan Python?


432

Saya akan mengekstrak semua angka yang terkandung dalam string. Mana yang lebih cocok untuk tujuan, ekspresi reguler atau isdigit()metode?

Contoh:

line = "hello 12 hi 89"

Hasil:

[12, 89]

Jawaban:


485

Jika Anda hanya ingin mengekstrak hanya bilangan bulat positif, coba yang berikut ini:

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

Saya berpendapat bahwa ini lebih baik daripada contoh regex karena tiga alasan. Pertama, Anda tidak perlu modul lain; kedua, ini lebih mudah dibaca karena Anda tidak perlu mengurai regex-bahasa mini; dan ketiga, lebih cepat (dan karenanya kemungkinan lebih pythonic):

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

Ini tidak akan mengenali float, bilangan bulat negatif, atau bilangan bulat dalam format heksadesimal. Jika Anda tidak dapat menerima batasan ini, jawaban ramping di bawah ini akan membantu.


5
ini akan gagal untuk kasus seperti "h3110 23 kucing 444,4 kelinci 11-2 anjing"
sharafjaffri

8
Kasus normatif menggunakan re. Ini adalah alat umum dan kuat (sehingga Anda belajar sesuatu yang sangat berguna). Kecepatan agak tidak relevan dalam parsing log (itu bukan pemecah angka intensif setelah semua), remodul ini di perpustakaan Python standar dan tidak ada salahnya memuatnya.
Ioannis Filippidis

19
Saya memiliki string seperti mumblejumble45mumblejumbledi mana saya tahu bahwa hanya ada satu nomor. Solusinya sederhana int(filter(str.isdigit, your_string)).
Jonas Lindeløv

1
Komentar minor: Anda mendefinisikan variabel stryang kemudian menimpa strobjek dan metode dalam basis python. Itu bukan praktik yang baik karena Anda mungkin membutuhkannya nanti dalam skrip.
Jonas Lindeløv

11
int(filter(...))akan meningkatkan TypeError: int() argument must be a string...untuk Python 3.5, sehingga Anda dapat menggunakan versi terbaru: int(''.join(filter(str.isdigit, your_string)))untuk mengekstraksi semua digit ke satu integer.
Mark Mishyn

449

Saya akan menggunakan regexp:

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

Ini juga akan cocok dengan 42 dari bla42bla. Jika Anda hanya ingin angka dibatasi oleh batas kata (spasi, titik, koma), Anda dapat menggunakan \ b:

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

Untuk berakhir dengan daftar nomor alih-alih daftar string:

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

9
... lalu petakan intdi atasnya dan Anda selesai. +1 terutama untuk bagian terakhir. Saya akan menyarankan string mentah ( r'\b\d+\b' == '\\b\\d+\\b').

5
Itu bisa dimasukkan ke dalam daftar dengan generator, seperti:int_list = [int(s) for s in re.findall('\\d+', 'hello 12 hi 89')]
GreenMatt

7
@GreenMatt: yang secara teknis daftar pemahaman (bukan generator), tapi saya setuju bahwa pemahaman / generator lebih Pythonic daripada map.
Seth Johnson

1
@Seth Johnson: Ups! Anda benar, saya salah mengetik dalam apa yang tampaknya pikiran yang berkabut. :-( Terima kasih atas koreksinya!
GreenMatt

2
Saya punya masalah. Bagaimana jika saya ingin mengekstrak angka float juga suka 1,45 di "hello1.45 hai". Ini akan memberi saya 1 dan 45 sebagai dua nomor yang berbeda
ab123

89

Ini lebih dari sedikit terlambat, tetapi Anda dapat memperluas ekspresi regex ke akun notasi ilmiah juga.

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)

Semuanya baik-baik saja!

Selain itu, Anda dapat melihat regex bawaan AWS Glue


1
Karena ini adalah satu-satunya jawaban yang disukai siapa pun, berikut adalah cara melakukannya dengan notasi Ilmiah "[- +]? \ D + [\.]? \ D * [Ee]? \ D *". Atau beberapa variasi. Selamat bersenang-senang!
aidan.plenert.macdonald

Temukan ada masalah dengan kasus paling sederhana misalnya s = "4"mengembalikan tidak cocok. Bisakah diedit juga untuk mengurus ini?
batFINGER

1
bagus tapi tidak menangani koma (mis.
74.600

Grup yang lebih verbose adalah Grup [+-]?\d*[\.]?\d*(?:(?:[eE])[+-]?\d+)?ini memang memberikan beberapa positif palsu (mis. +Kadang-kadang ditangkap dengan sendirinya), tetapi mampu menangani lebih banyak bentuk, seperti .001, plus itu tidak menggabungkan angka secara otomatis (seperti dalam s=2+1)
DavisDude

24
Ah ya, yang jelas [-+]?[.]?[\d]+(?:,\d\d\d)*[\.]?\d*(?:[eE][-+]?\d+)?- konyol saya ... bagaimana mungkin saya tidak memikirkan itu?
Przemek D

70

Saya berasumsi Anda ingin mengapung bukan hanya bilangan bulat, jadi saya akan melakukan sesuatu seperti ini:

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

Perhatikan bahwa beberapa solusi lain yang diposting di sini tidak bekerja dengan angka negatif:

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

>>> '-3'.isdigit()
False

Ini menemukan mengapung dan bilangan bulat positif dan negatif. Untuk bilangan bulat positif dan negatif saja, ubah floatke int.
Hugo

3
Untuk angka negatif:re.findall("[-\d]+", "1 -2")
ytpillai

Apakah ada bedanya jika kita menulis continuealih-alih passdi dalam loop?
D. Jones

Ini menangkap lebih dari sekadar bilangan bulat positif, tetapi menggunakan split () akan kehilangan angka yang memiliki simbol mata uang mendahului digit pertama tanpa spasi, yang umum dalam dokumen keuangan
Marc Maxmeister

Tidak berfungsi untuk pelampung yang tidak memiliki ruang dengan karakter lain, contoh: '4,5 k hal' akan bekerja, '4,5 k hal' tidak akan berfungsi.
Jay D.

64

Jika Anda tahu itu hanya akan menjadi satu angka dalam string, yaitu 'halo 12 hai', Anda dapat mencoba memfilter.

Sebagai contoh:

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

Tapi hati-hati !!! :

In [4]: int(''.join(filter(str.isdigit, '200 grams 5')))
Out[4]: 2005

12
Dalam Python 3.6.3 saya mendapat TypeError: int() argument must be a string, a bytes-like object or a number, not 'filter'- memperbaikinya dengan menggunakanint("".join(filter(str.isdigit, '200 grams')))
Kent Munthe Caspersen

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
Selamat datang di SO dan terima kasih telah mengirim jawaban. Itu selalu praktik yang baik untuk menambahkan beberapa komentar tambahan ke jawaban Anda dan mengapa itu memecahkan masalah, bukan hanya memposting potongan kode.
sebs

tidak bekerja dalam kasus saya. tidak jauh berbeda dari jawaban di atas
oldboy

ValueError: tidak dapat mengonversi string menjadi float: 'e' dan itu tidak berfungsi dalam beberapa kasus :(
Vilq

15

Saya sedang mencari solusi untuk menghilangkan topeng string, khususnya dari nomor telepon Brasil, posting ini tidak dijawab tetapi menginspirasi saya. Ini solusi saya:

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

12

Menggunakan Regex di bawah ini adalah caranya

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)

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

Anda setidaknya harus mengkompilasi regex jika Anda tidak menggunakanfindall()
information_interchange

2
repl_str = re.compile('\d+.?\d*') seharusnya: repl_str = re.compile('\d+\.?\d*') Untuk contoh yang dapat direproduksi menggunakan 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)

Hai,

Anda dapat mencari semua bilangan bulat dalam string melalui digit dengan menggunakan ekspresi findall.

Pada langkah kedua buat daftar res2 dan tambahkan angka yang ditemukan dalam string ke daftar ini

semoga ini membantu

Salam, Diwakar Sharma


Jawaban yang diberikan ditandai untuk ditinjau sebagai Pos Kualitas Rendah. Berikut adalah beberapa panduan untuk Bagaimana saya menulis jawaban yang baik? . Jawaban yang diberikan ini mungkin benar, tetapi bisa mendapat manfaat dari penjelasan. Jawaban hanya kode tidak dianggap sebagai jawaban "baik". Dari ulasan .
Trenton McKinney

solusi sederhana dan berhasil, dihargai
moyo

7

Jawaban ini juga berisi huruf besar-kecil ketika angka tersebut mengambang dalam string

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

Saya kagum melihat bahwa belum ada yang menyebut penggunaan itertools.groupbysebagai alternatif untuk mencapai ini.

Anda dapat menggunakan itertools.groupby()bersama dengan str.isdigit()untuk mengekstraksi angka dari string sebagai:

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]

Nilai yang dipegang oleh lakan:

[12, 89]

PS: Ini hanya untuk tujuan ilustrasi untuk menunjukkan bahwa sebagai alternatif kita juga bisa menggunakan groupbyuntuk mencapainya. Tapi ini bukan solusi yang disarankan. Jika Anda ingin mencapai ini, Anda harus menggunakan jawaban fmark yang diterima berdasarkan menggunakan pemahaman daftar dengan str.isdigitsebagai filter.


4

Saya hanya menambahkan jawaban ini karena tidak ada yang menambahkan satu menggunakan penanganan Exception dan karena ini juga berfungsi untuk mengapung

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

Keluaran:

[1234.0, 56.78]

4

Untuk menangkap pola yang berbeda, sangat membantu untuk menanyakan dengan pola yang berbeda.

Atur semua pola yang menangkap berbagai pola minat:

(temukan koma) 12.300 atau 12.300,00

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

(temukan mengapung) 0.123 atau .123

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

(menemukan bilangan bulat) 123

'[\ d] +'

Gabungkan dengan pipa (|) menjadi satu pola dengan banyak atau kondisional .

(Catatan: Prioritaskan pola-pola kompleks terlebih dahulu. Pola-pola sederhana akan mengembalikan potongan tangkapan kompleks alih-alih tangkapan kompleks mengembalikan tangkapan penuh).

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

Di bawah ini, kami akan mengonfirmasi adanya pola re.search(), lalu mengembalikan daftar tangkapan yang dapat diubah. Akhirnya, kami akan mencetak setiap tangkapan menggunakan notasi braket untuk men-subseleksi nilai pengembalian objek yang cocok dari objek yang cocok.

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

Pengembalian:

33
42
32
30
444.4
12,001

2

Karena tidak ada yang berurusan dengan angka keuangan dunia nyata dalam dokumen excel dan word yang perlu saya temukan, inilah variasi saya. Ia menangani int, float, angka negatif, angka mata uang (karena tidak membalas secara terpisah), dan memiliki opsi untuk menjatuhkan bagian desimal dan hanya mengembalikan int, atau mengembalikan semuanya.

Ini juga menangani sistem angka Indian Laks di mana koma muncul secara tidak teratur, tidak setiap 3 angka terpisah.

Itu tidak menangani notasi ilmiah atau angka negatif yang dimasukkan ke dalam tanda kurung dalam anggaran - akan tampak positif.

Itu juga tidak mengekstrak tanggal. Ada cara yang lebih baik untuk menemukan tanggal dalam string.

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, saya menyukai jawaban Anda, tetapi tidak menemukan pelampung. Saya sedang mengerjakan skrip untuk mem-parsing kode menuju pabrik CNC dan perlu menemukan dimensi X dan Y yang bisa bilangan bulat atau mengapung, jadi saya menyesuaikan kode Anda dengan yang berikut. Ini menemukan int, mengapung dengan vals positif dan negatif. Masih tidak menemukan nilai hex berformat tetapi Anda dapat menambahkan "x" dan "A" melalui "F" ke num_chartuple dan saya pikir itu akan mengurai hal-hal seperti '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

Pilihan terbaik yang saya temukan di bawah. Ini akan mengekstrak angka dan dapat menghilangkan semua jenis char.

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

Untuk nomor telepon, Anda cukup mengecualikan semua karakter non-digit dengan \ D di regex:

import re

phone_number = '(619) 459-3635'
phone_number = re.sub(r"\D", "", phone_number)
print(phone_number)
Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.