Làm cách nào để trích xuất số từ một chuỗi trong Python?


432

Tôi sẽ trích xuất tất cả các số có trong một chuỗi. Cái nào phù hợp hơn cho mục đích, biểu thức chính quy hay isdigit()phương thức?

Thí dụ:

line = "hello 12 hi 89"

Kết quả:

[12, 89]

Câu trả lời:


485

Nếu bạn chỉ muốn trích xuất các số nguyên dương, hãy thử như sau:

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

Tôi sẽ lập luận rằng điều này tốt hơn ví dụ regex vì ba lý do. Đầu tiên, bạn không cần một mô-đun khác; thứ hai, nó dễ đọc hơn vì bạn không cần phải phân tích ngôn ngữ mini regex; và thứ ba, nó nhanh hơn (và do đó có khả năng nhiều pythonic hơn):

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

Điều này sẽ không nhận ra số float, số nguyên âm hoặc số nguyên ở định dạng thập lục phân. Nếu bạn không thể chấp nhận những hạn chế này, câu trả lời của slim dưới đây sẽ thực hiện mẹo.


5
điều này sẽ thất bại trong trường hợp như "h3110 23 cat 444.4 rabbit 11-2 dog"
sharafjaffri 4/12/13

8
Các trường hợp quy phạm đang sử dụng re. Nó là một công cụ chung và mạnh mẽ (vì vậy bạn học được điều gì đó rất hữu ích). Tốc độ có phần không liên quan trong phân tích cú pháp nhật ký (rốt cuộc nó không phải là một số bộ giải số chuyên sâu), remô-đun nằm trong thư viện Python tiêu chuẩn và việc tải nó không ảnh hưởng gì.
Ioannis Filippidis

19
Tôi có các chuỗi như mumblejumble45mumblejumbletrong đó tôi biết rằng chỉ có một số. Giải pháp đơn giản int(filter(str.isdigit, your_string)).
Jonas Lindeløv

1
Một nhận xét nhỏ: bạn xác định biến strsau đó ghi đè strđối tượng và phương thức trong python cơ sở. Đó không phải là thực hành tốt vì bạn có thể cần nó sau này trong kịch bản.
Jonas Lindeløv

11
int(filter(...))sẽ nâng cao TypeError: int() argument must be a string...cho Python 3.5, vì vậy bạn có thể sử dụng phiên bản cập nhật: int(''.join(filter(str.isdigit, your_string)))để trích xuất tất cả các chữ số thành một số nguyên.
Đánh dấu Mishyn

448

Tôi sẽ sử dụng một biểu thức chính quy:

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

Điều này cũng sẽ phù hợp với 42 từ bla42bla. Nếu bạn chỉ muốn các số được phân tách bằng ranh giới từ (dấu cách, dấu chấm, dấu phẩy), bạn có thể sử dụng \ b:

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

Để kết thúc với một danh sách các số thay vì danh sách các chuỗi:

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

9
... Và sau đó ánh xạ intqua nó và bạn đã hoàn thành. +1 đặc biệt cho phần sau. Tôi muốn đề xuất chuỗi thô ( r'\b\d+\b' == '\\b\\d+\\b') mặc dù.

5
Nó có thể được đưa vào một danh sách với một trình tạo, chẳng hạn như:int_list = [int(s) for s in re.findall('\\d+', 'hello 12 hi 89')]
GreenMatt

7
@GreenMatt: về mặt kỹ thuật là một sự hiểu biết danh sách (không phải là một trình tạo), nhưng tôi đồng ý rằng các hiểu biết / trình tạo nhiều Pythonic hơn map.
Seth Johnson

1
@Seth Johnson: Rất tiếc! Bạn nói đúng, tôi lầm tưởng trong tâm trạng rõ ràng là sương mù. :-( Cảm ơn bạn đã sửa chữa!
GreenMatt

2
Tôi có một vấn đề mặc dù. Điều gì sẽ xảy ra nếu tôi muốn trích xuất số float cũng như 1.45 trong "hello1.45 hi". Nó sẽ cho tôi 1 và 45 là hai số khác nhau
ab123

89

Điều này muộn hơn một chút, nhưng bạn cũng có thể mở rộng biểu thức regex để giải thích cho ký hiệu khoa học.

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)

Cho tất cả tốt!

Ngoài ra, bạn có thể xem biểu thức chính quy tích hợp keo AWS


1
Vì đây là câu trả lời duy nhất mà bất cứ ai thích, đây là cách thực hiện với ký hiệu Khoa học "[- +]? \ D + [\.]? \ D * [Ee]? \ D *". Hoặc một số biến thể. Chúc vui vẻ!
Aidan.plenert.macdonald 6/11/2015

Tìm có một vấn đề với trường hợp đơn giản nhất, ví dụ s = "4"trả về không có kết quả khớp. Có thể được chỉnh sửa để cũng chăm sóc này?
batFinger

1
tốt nhưng nó không xử lý dấu phẩy (ví dụ 74.600)
yekta

Một nhóm dài dòng hơn là [+-]?\d*[\.]?\d*(?:(?:[eE])[+-]?\d+)?Nhóm này đưa ra một số dương tính giả ( +đôi khi bị bắt bởi chính nó), nhưng có thể xử lý nhiều hình thức hơn, như .001, cộng với việc nó không kết hợp các số tự động (như trong s=2+1)
DavisDude

24
À đúng rồi, điều hiển nhiên [-+]?[.]?[\d]+(?:,\d\d\d)*[\.]?\d*(?:[eE][-+]?\d+)?- thật ngớ ngẩn với tôi ... làm sao tôi không nghĩ đến điều đó?
Przemek D

70

Tôi giả sử bạn muốn số float không chỉ là số nguyên nên tôi sẽ làm một cái gì đó như thế này:

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

Lưu ý rằng một số giải pháp khác được đăng ở đây không hoạt động với số âm:

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

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

Điều này tìm thấy số phao và số nguyên dương và âm. Đối với số nguyên dương và âm, thay đổi floatthành int.
Hugo

3
Đối với số âm:re.findall("[-\d]+", "1 -2")
ytpillai

Có làm cho bất kỳ sự khác biệt nếu chúng ta viết continuethay vì passtrong vòng lặp?
D. Jones

Điều này thu hút nhiều hơn chỉ các số nguyên dương, nhưng sử dụng split () sẽ bỏ lỡ các số có ký hiệu tiền tệ trước chữ số đầu tiên không có khoảng
trắng

Không hoạt động đối với những chiếc phao không có không gian với các nhân vật khác, ví dụ: '4,5 k thứ' sẽ hoạt động, '4,5k thứ' sẽ không.
Jay D.

64

Nếu bạn biết đó sẽ chỉ là một số trong chuỗi, tức là 'xin chào 12 hi', bạn có thể thử bộ lọc.

Ví dụ:

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

Nhưng hãy cẩn thận !!! :

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

12
Trong Python 3.6.3 tôi đã nhận được TypeError: int() argument must be a string, a bytes-like object or a number, not 'filter'- sửa nó bằng cách sử dụngint("".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
Chào mừng bạn đến với SO và cảm ơn vì đã đăng câu trả lời. Luôn luôn là một thực hành tốt để thêm một số nhận xét bổ sung vào câu trả lời của bạn và lý do tại sao nó giải quyết vấn đề, thay vì chỉ đăng một đoạn mã.
sebs

đã không làm việc trong trường hợp của tôi. không khác nhiều so với câu trả lời ở trên
oldboy

ValueError: không thể chuyển đổi chuỗi thành float: 'e' và nó không hoạt động trong một số trường hợp :(
Vilq

15

Tôi đang tìm kiếm một giải pháp để loại bỏ mặt nạ của chuỗi, cụ thể là từ số điện thoại của Brazil, bài đăng này không trả lời nhưng đã truyền cảm hứng cho tôi. Đây là giải pháp của tôi:

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

12

Sử dụng Regex dưới đây là cách

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)

với 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']

Ít nhất bạn nên biên dịch regex nếu bạn không sử dụngfindall()
information_interchange

2
repl_str = re.compile('\d+.?\d*') nên là: repl_str = re.compile('\d+\.?\d*') Đối với một ví dụ có thể tái tạo bằng 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)

Chào ,

bạn có thể tìm kiếm tất cả các số nguyên trong chuỗi thông qua chữ số bằng cách sử dụng biểu thức findall.

Trong bước thứ hai, tạo một danh sách res2 và thêm các chữ số được tìm thấy trong chuỗi vào danh sách này

hi vọng điêu nay co ich

Trân trọng, Diwakar Sharma


Câu trả lời được cung cấp đã được gắn cờ để đánh giá là Bài đăng chất lượng thấp. Dưới đây là một số hướng dẫn cho Làm thế nào để tôi viết một câu trả lời tốt? . Câu trả lời được cung cấp này có thể đúng, nhưng nó có thể được hưởng lợi từ một lời giải thích. Mã chỉ trả lời không được coi là câu trả lời "tốt". Từ đánh giá .
Trenton McKinney

giải pháp đơn giản và hiệu quả, được đánh giá cao
moyo

7

Câu trả lời này cũng chứa trường hợp khi số nổi trong chuỗi

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

Tôi ngạc nhiên khi thấy rằng chưa có ai đề cập đến việc sử dụng itertools.groupbynhư là một thay thế để đạt được điều này.

Bạn có thể sử dụng itertools.groupby()cùng với str.isdigit()để trích xuất số từ chuỗi dưới dạng:

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]

Giá trị được giữ bởi lsẽ là:

[12, 89]

PS: Đây chỉ là mục đích minh họa để cho thấy rằng như là một thay thế chúng ta cũng có thể sử dụng groupbyđể đạt được điều này. Nhưng đây không phải là một giải pháp được đề nghị. Nếu bạn muốn đạt được điều này, bạn nên sử dụng câu trả lời được chấp nhận của fmark dựa trên việc sử dụng tính năng hiểu danh sách với str.isdigitbộ lọc.


4

Tôi chỉ thêm câu trả lời này vì không ai thêm ai bằng cách sử dụng Xử lý ngoại lệ và vì điều này cũng hoạt động cho phao

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

Đầu ra:

[1234.0, 56.78]

3

Để bắt các mẫu khác nhau, thật hữu ích khi truy vấn với các mẫu khác nhau.

Thiết lập tất cả các mẫu bắt các mẫu số quan tâm khác nhau:

(tìm thấy dấu phẩy) 12.300 hoặc 12.300.00

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

(tìm thấy số float) 0.123 hoặc .123

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

(tìm số nguyên) 123

'[\ D] +'

Kết hợp với ống (|) thành một mẫu có nhiều hoặc có điều kiện .

(Lưu ý: Đặt các mẫu phức tạp trước, các mẫu đơn giản khác sẽ trả về các khối của sản phẩm khai thác phức tạp thay vì sản phẩm khai thác phức tạp trả lại toàn bộ sản phẩm khai thác).

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

Dưới đây, chúng tôi sẽ xác nhận một mẫu có mặt re.search(), sau đó trả về một danh sách các sản phẩm khai thác có thể lặp lại. Cuối cùng, chúng tôi sẽ in từng lần bắt bằng cách sử dụng ký hiệu ngoặc để chọn giá trị trả về của đối tượng khớp từ đối tượng khớp.

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

Trả về:

33
42
32
30
444.4
12,001

2

Vì không ai trong số này xử lý các số tài chính trong thế giới thực trong các tài liệu excel và word mà tôi cần tìm, nên đây là biến thể của tôi. Nó xử lý ints, float, số âm, số tiền (vì nó không trả lời khi tách) và có tùy chọn bỏ phần thập phân và chỉ trả về int hoặc trả lại mọi thứ.

Nó cũng xử lý hệ thống số Laks Ấn Độ trong đó dấu phẩy xuất hiện không đều, không cách nhau 3 số.

Nó không xử lý ký hiệu khoa học hoặc số âm được đặt trong ngoặc đơn trong ngân sách - sẽ xuất hiện tích cực.

Nó cũng không trích xuất ngày. Có nhiều cách tốt hơn để tìm ngày trong chuỗi.

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, tôi thích câu trả lời của bạn, nhưng nó không tìm thấy phao. Tôi đang làm việc trên một kịch bản để phân tích mã đi đến nhà máy CNC và cần tìm cả hai kích thước X và Y có thể là số nguyên hoặc số float, vì vậy tôi đã điều chỉnh mã của bạn theo sau. Điều này tìm thấy int, float với vals tích cực và tiêu cực. Vẫn không tìm thấy các giá trị được định dạng hex nhưng bạn có thể thêm "x" và "A" đến "F" vào num_charbộ dữ liệu và tôi nghĩ rằng nó sẽ phân tích những thứ như '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

Tùy chọn tốt nhất tôi tìm thấy là dưới đây. Nó sẽ trích xuất một số và có thể loại bỏ bất kỳ loại char nào.

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

Đối với số điện thoại, bạn có thể chỉ cần loại trừ tất cả các ký tự không có chữ số với \ D trong regex:

import re

phone_number = '(619) 459-3635'
phone_number = re.sub(r"\D", "", phone_number)
print(phone_number)
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.