質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

ただいまの
回答率

88.05%

python 数字が一か所入っている文字列から数字を取り出す一番処理が軽い方法ってなんですか?

解決済

回答 4

投稿

  • 評価
  • クリップ 1
  • VIEW 409

score 119

python で数字が一か所入っている文字列sがあるとします。
なので、これはs

'1yen'
'23dogs'
'there is 45 dogs'


これはsではないです

'1 2'
'1 and 2 and 3 and 4'
'1or5'


このsから数字だけを取り出したいです。この場合、一番処理が軽い方法は正規表現ですか?それとも他にもっと軽いやり方はありますか?

  • 気になる質問をクリップする

    クリップした質問は、後からいつでもマイページで確認できます。

    またクリップした質問に回答があった際、通知やメールを受け取ることができます。

    クリップを取り消します

  • 良い質問の評価を上げる

    以下のような質問は評価を上げましょう

    • 質問内容が明確
    • 自分も答えを知りたい
    • 質問者以外のユーザにも役立つ

    評価が高い質問は、TOPページの「注目」タブのフィードに表示されやすくなります。

    質問の評価を上げたことを取り消します

  • 評価を下げられる数の上限に達しました

    評価を下げることができません

    • 1日5回まで評価を下げられます
    • 1日に1ユーザに対して2回まで評価を下げられます

    質問の評価を下げる

    teratailでは下記のような質問を「具体的に困っていることがない質問」、「サイトポリシーに違反する質問」と定義し、推奨していません。

    • プログラミングに関係のない質問
    • やってほしいことだけを記載した丸投げの質問
    • 問題・課題が含まれていない質問
    • 意図的に内容が抹消された質問
    • 過去に投稿した質問と同じ内容の質問
    • 広告と受け取られるような投稿

    評価が下がると、TOPページの「アクティブ」「注目」タブのフィードに表示されにくくなります。

    質問の評価を下げたことを取り消します

    この機能は開放されていません

    評価を下げる条件を満たしてません

    評価を下げる理由を選択してください

    詳細な説明はこちら

    上記に当てはまらず、質問内容が明確になっていない質問には「情報の追加・修正依頼」機能からコメントをしてください。

    質問の評価を下げる機能の利用条件

    この機能を利用するためには、以下の事項を行う必要があります。

回答 4

checkベストアンサー

+1

最速かどうかはわかりませんが、正規表現が簡単で実用的な速度が得られるでしょう。

[0-9]+\d+などがわかりやすいですが、細かい要件は詰めておく必要があります(全角、漢数字など気にすることは色々あります)。

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

0

正規表現以外のやり方もありますが、軽いかどうかわかりません。

def match(s):
    a = -1
    b = n = len(s)
    for i in range(n):
        if s[i] >= '0' and s[i] <= '9':
            if a < 0:
                a = i
            elif b < n:
                return None
        elif a >= 0 and b == n:
            b = i
    return None if a < 0 else s[a:b]

a = [ '1yen', '23dogs', 'there is 45 dogs', '1 2',
    '1 and 2 and 3 and 4', '1or5' ]
r = [ match(s) for s in a ]
print(a)
print(r)


正規表現を使うと

import re

class Dig:
    pat = re.compile(r"^\D*(\d+)\D*$")
    def match(s):
        mat = Dig.pat.match(s)
        return mat.group(1) if mat else None

a = [ '1yen', '23dogs', 'there is 45 dogs', '1 2',
    '1 and 2 and 3 and 4', '1or5' ]
r = [ Dig.match(s) for s in a ]
print(a)
print(r)


どちらが軽いか(速いか?)は自分で測定してみてください。

追記
質問を「与えられた文字列の中に、数字列が1個所だけあればその数字列を返し、
数字列が無いまたは2個所以上ある場合は何も返さない」だと誤解していました。

数字列が必ず1個所だけある文字列が与えられるのですね。

import re

pat = re.compile(r"\D")

a = [ '1yen', '23dogs', 'there is 45 dogs' ]
r = [ pat.sub("", s) for s in a ]
print(a)
print(r)


正規表現を使わないで、数字列の開始点と終了点を探すと、

def match(s):
    a = -1
    b = n = len(s);
    for i in range(n):
        if s[i].isdigit():
            if a < 0:
                a = i
        elif a >= 0:
            b = i
            break
    return s[a:b]

a = [ '1yen', '23dogs', 'there is 45 dogs' ]
r = [ match(s) for s in a ]
print(a)
print(r)

投稿

編集

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

0

軽いかどうかは分からないけど、リスト内包表記で。

>>> def digit(s):
...     return ''.join([c for c in s if c.isdigit()])
...
>>> digit('1yen')
'1'
>>> digit('23dogs')
'23'
>>> digit('there is 45 dogs')
'45'

ジェネレータ式でもできるけど、内包表記の方が早い気がします。

>>> def digit(s):
...     return ''.join(c for c in s if c.isdigit())
...
>>> digit('1yen')
'1'
>>> digit('23dogs')
'23'
>>> digit('there is 45 dogs')
'45'

投稿

編集

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

0

先程の回答は質問を読み違えてたので、コードのみ再投稿します。

  • take_s: itertools.groupby で 数字と数字以外に分離する方法。
  • take_s_re: 正規表現を使った方法。
import re
import string
import itertools

# 全角文字も含む場合
# key=str.isdigit
#
# 半角のみ
# key=string.digits.__contains__


def take_s(s, key=string.digits.__contains__,
           groupby=itertools.groupby,
           result=None, strjoin="".join):
    """
    >>> take_s('1yen')
    1
    >>> take_s('23dogs')
    23
    >>> take_s('there is 45 dogs')
    45
    >>> take_s('zero 0')
    0

    >>> take_s('1 2')
    >>> take_s('1 and 2 and 3 and 4')
    >>> take_s('1or5')
    >>> take_s('zero 0 zero 0')
    """

    for isdigit, digits in groupby(s, key):
        if isdigit: # 数値のグループ
            if result is not None: # 2個目以降の数値が見つかった場合 None
                return
            else:
                result = int(strjoin(digits))
    return result


def take_s_re(s, match=re.compile(r"^\D*(\d+)\D*$").match):
    """
    >>> take_s_re('1yen')
    1
    >>> take_s_re('23dogs')
    23
    >>> take_s_re('there is 45 dogs')
    45
    >>> take_s_re('zero 0')
    0

    >>> take_s_re('1 2')
    >>> take_s_re('1 and 2 and 3 and 4')
    >>> take_s_re('1or5')
    >>> take_s_re('zero 0 zero 0')
    """
    # py3.8) if m := match(s):
    m = match(s)
    if m:
        return int(m.group(1))


if __name__ == "__main__":
    import doctest
    doctest.testmod()

イメージ説明


追記: 試していない事

  • pypy や Numba 等での JITコンパイル。
  • 全角数字を対象にした場合の、正規表現 vs str.isdigit

投稿

編集

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

15分調べてもわからないことは、teratailで質問しよう!

  • ただいまの回答率 88.05%
  • 質問をまとめることで、思考を整理して素早く解決
  • テンプレート機能で、簡単に質問をまとめられる

関連した質問

同じタグがついた質問を見る