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]

これは、3つの理由で正規表現の例よりも優れていると主張します。まず、別のモジュールは必要ありません。次に、正規表現のミニ言語を解析する必要がないため、読みやすくなります。第三に、それはより高速です(したがって、より多くの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

これは、浮動小数点数、負の整数、または16進形式の整数を認識しません。これらの制限を受け入れることができない場合は、以下のslimの答えがうまくいきます。


5
これは、「h3110 23猫444.4ウサギ11-2犬」のような場合に失敗します
sharafjaffri

8
規範的な場合はを使用していreます。これは一般的で強力なツールです(そのため、非常に役立つものを学びます)。速度はログの解析に多少関係ありません(結局、集中的な数値ソルバーreではありません)。モジュールは標準のPythonライブラリにあり、ロードしても問題ありません。
Ioannis Filippidis 14

19
mumblejumble45mumblejumble数字が1つしかないことを知っているような文字列がありました。解決策は単純int(filter(str.isdigit, your_string))です。
JonasLindeløv15年

1
マイナーコメント:変数strを定義するstrと、ベースPythonのオブジェクトとメソッドがオーバーライドされます。スクリプトの後半で必要になる可能性があるため、これは良い方法ではありません。
JonasLindeløv2015

11
int(filter(...))発生しますTypeError: int() argument must be a string...:あなたが更新されたバージョンを使用することができますので、Pythonの3.5のためのint(''.join(filter(str.isdigit, your_string)))1つの整数にすべての桁を抽出します。
Mark Mishyn 2017年

448

私は正規表現を使用します:

>>> 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')]
GreenMatt

7
@GreenMatt:技術的にはリストの内包表記(ジェネレーターではない)ですが、内包表記/ジェネレーターはよりもPython的であることに同意しmapます。
セスジョンソン

1
@Seth Johnson:おっと!あなたは正しい、私はどうやら曇った心の状態であったものを間違ってタイプした。:-(訂正ありがとうございました!
GreenMatt 2010年

2
でも問題があります。「hello1.45 hi」の1.45のような浮動小数点数を抽出したい場合はどうすればよいですか。これは、2つの異なる番号として私に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 2015年

最も単純なケースで問題が発生していることを確認しs = "4"ます(一致が返されないなど)。これを処理するために再編集できますか?
batFINGER 2016年

1
いいですが、コンマ(例:74,600)を処理しません
yekta

より詳細なグループは次のとおりです。[+-]?\d*[\.]?\d*(?:(?:[eE])[+-]?\d+)?このグループはいくつかの誤+.001s=2+1
検知を提供

24
ああ、明らかです[-+]?[.]?[\d]+(?:,\d\d\d)*[\.]?\d*(?:[eE][-+]?\d+)?-私の愚かさ...どうしてそれを考えられなかったのですか?
Przemek D 2017

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

これは正と負の浮動小数点数と整数を見つけます。正と負の整数のみの場合は、に変更floatintます。
Hugo

3
負の数の場合:re.findall("[-\d]+", "1 -2")
ytpillai

ループのcontinue代わりに書き込む場合、何か違いpassはありますか?
D.ジョーンズ

これは単なる正の整数以上のものをキャッチしますが、split()を使用すると、最初の桁の前にスペースのない通貨記号が含まれる数値が
失わ

他の文字とスペースのないフロートには機能しません。例:「4.5 kモノ」は機能し、「4.5 kモノ」は機能しません。
ジェイD.

64

文字列内の数値が1つだけであることがわかっている場合(つまり、「hello 12 hi」)、フィルターを試すことができます。

例えば:

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')))
ケントムンテ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
SOへようこそ。回答を投稿していただきありがとうございます。単にコードスニペットを投稿するのではなく、回答にいくつかのコメントを追加し、なぜそれが問題を解決するかを常にお勧めします。
18年

私の場合はうまくいきませんでした。上記の答えとそれほど変わらない
oldboy

ValueError:文字列をfloat: 'e'に変換できず、場合によっては機能しません:(
Vilq

15

特にブラジルの電話番号から文字列のマスクを削除するための解決策を探していましたが、この投稿には回答がありませんでしたが、刺激を受けました。これは私の解決策です:

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

12

以下の正規表現を使用する方法です

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)

フィンダル 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」
アレクシスLucattini

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

こんにちは 、

findall式を使用して、文字列内のすべての整数を数字で検索できます。

2番目のステップでは、リストres2を作成し、文字列で見つかった数字をこのリストに追加します

お役に立てれば

よろしく、Diwakar Sharma


提供された回答には、低品質の投稿としてレビュー用のフラグが付けられました。ここではいくつかのためのガイドラインです、私は良い答えを書くにはどうすればよいですか?。この提供された答えは正しいかもしれませんが、説明から利益を得る可能性があります。コードのみの回答は「良い」回答とは見なされません。レビューから。
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

例外処理を使用して誰も追加せず、これがフロートでも機能するため、私はこの回答を追加しています

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

(floatを検出)0.123または.123

'[\ d] * [。] [\ d] +'

(整数を見つける)123

'[\ d] +'

パイプ(|)を組み合わせて、複数または条件付きの 1つのパターンにします

(注:複雑なパターンを最初に置くと、単純なパターンは、複雑なキャッチが完全なキャッチを返すのではなく、複雑なキャッチのチャンクを返します)。

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 Docの実際の財務数値を扱っていなかったため、ここに私のバリエーションを示します。int、float、負の数、通貨数を処理し(分割時に応答しないため)、小数部を削除してintを返すか、すべてを返すかを選択できます。

また、カンマが不規則に出現するインディアンラックの数体系も処理します。

それは科学表記法または予算の括弧内に置かれた負の数を扱いません-正に見えます。

また、日付は抽出されません。文字列の日付を見つけるには、より良い方法があります。

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、私はあなたの答えが好きでしたが、浮動小数点数は見つかりませんでした。私はCNCミルに行くコードを解析するスクリプトに取り組んでおり、整数または浮動小数点数のXとYの両方の次元を見つける必要があったので、コードを次のように適合させました。これは、正の値と負の値を持つint、floatを見つけます。それでも16進数形式の値は見つかりませんが、 "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

私が見つけた最良のオプションは以下です。数値を抽出し、あらゆる種類の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

電話番号の場合、正規表現で\ Dを使用して数字以外のすべての文字を単に除外できます。

import re

phone_number = '(619) 459-3635'
phone_number = re.sub(r"\D", "", phone_number)
print(phone_number)
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.