2つのマーカー間の部分文字列を抽出する方法は?


334

文字列が'gfgfdAAA1234ZZZuijjk'あり、その'1234'部分だけを抽出したいとします。

私が興味を持っている部分の直前AAAと直後のいくつかのキャラクターがどうなるかを知っているだけZZZです1234

sed文字列で、このような何かを行うことが可能です。

echo "$STRING" | sed -e "s|.*AAA\(.*\)ZZZ.*|\1|"

そして、これは1234結果として私に与えます。

Pythonで同じことをするには?

回答:


585

-正規表現の使用説明書をさらに参照するために

import re

text = 'gfgfdAAA1234ZZZuijjk'

m = re.search('AAA(.+?)ZZZ', text)
if m:
    found = m.group(1)

# found: 1234

または:

import re

text = 'gfgfdAAA1234ZZZuijjk'

try:
    found = re.search('AAA(.+?)ZZZ', text).group(1)
except AttributeError:
    # AAA, ZZZ not found in the original string
    found = '' # apply your error handling

# found: 1234

20
2番目のソリューションは、パターンがほとんどの場合一致する場合、許可よりも許しを求めるほうが簡単なので、より優れています。
Bengt

7
インデックス作成は0から始まりませんか?したがって、group(1)の代わりにgroup(0)を使用する必要がありますか?
アレクサンダー

22
@Alexander、いいえ、group(0)は完全に一致する文字列を返します:AAA1234ZZZ、group(1)は最初のグループに一致した文字のみを返します:1234
Yurii K

1
@Bengt:なぜですか?最初の解決策は私には非常に単純に見え、コードの行数が少なくなっています。
HelloGoodbye 2016

5
この表現では?+を貪欲でないように変更します。1以上の任意の回数だけ一致しますが、できる限り少なく、必要なだけ拡張します。?がない場合、最初のグループはgfgfAAA2ZZZkeAAA43ZZZonifeを2ZZZkeAAA43として一致しますが、?それだけにして、複数の検索(またはそれが取り除かと再度検索有する)43.一致し、2と一致するであろう
ドム

113
>>> s = 'gfgfdAAA1234ZZZuijjk'
>>> start = s.find('AAA') + 3
>>> end = s.find('ZZZ', start)
>>> s[start:end]
'1234'

その後、必要に応じてreモジュールでregexpsを使用することもできますが、それはあなたのケースでは必要ありません。


9
この質問は、入力テキストには常に「AAA」と「ZZZ」の両方が含まれることを示唆しているようです。これが当てはまらない場合、あなたの答えはひどく失敗します(つまり、空の文字列や例外をスローするのではなく、何か完全に間違ったものを返します。入力文字列として「こんにちは」と考えてください)。
tzot

@ user225312しかし、reメソッドは速くありませんか?
confused00

1
賛成投票ですが、保守性のために「s.find( 'AAA')+ 3」の代わりに「x = 'AAA'; s.find(x)+ len(x)」を使用します。
Alex

1
にトークンが見つからない場合はss.findが返され-1ます。スライシング演算子s[begin:end] はそれを有効なインデックスとして受け入れ、望ましくない部分文字列を返します。
ribamar 2017

@ confused00の検索は、restackoverflow.com
questions / 4901523 /…

63

正規表現

import re

re.search(r"(?<=AAA).*?(?=ZZZ)", your_text).group(0)

上記のAttributeError「現状のまま」は、「AAA」と「ZZZ」がない場合に失敗しますyour_text

文字列メソッド

your_text.partition("AAA")[2].partition("ZZZ")[0]

上に「AAA」または「ZZZ」が存在しない場合、上記は空の文字列を返しyour_textます。

PS Pythonチャレンジ?


6
この答えはおそらくより多くの賛成票に値します。文字列方式が最も堅牢な方法です。try / exceptは必要ありません。
ChaimG、2015

...いいけど限られた パーティションは正規表現ベースではないため、検索文字列が固定リテラルによって制限されていたため、このインスタンスでのみ機能します
GreenAsJade

ありがとうございます。-これは文字列に対して機能し、正規表現を必要としません
Alex

ああ、神様!本当に、パーティション!どうもありがとう!
Andrey Wal

15
import re
print re.search('AAA(.*?)ZZZ', 'gfgfdAAA1234ZZZuijjk').group(1)

1
AttributeError: 'NoneType' object has no attribute 'groups'-文字列にAAA、ZZZがない場合
eumiro

12

誰もこれを言及していないので、これは一度限りのスクリプトの簡単なバージョンです:

>>> x = 'gfgfdAAA1234ZZZuijjk'
>>> x.split('AAA')[1].split('ZZZ')[0]
'1234'

@ user1810100は、これを投稿する前の日からほぼ正確に5年前と言っていました...
John

10

1行のコードで実行できます

>>> import re

>>> re.findall(r'\d{1,5}','gfgfdAAA1234ZZZuijjk')

>>> ['1234']

結果はリストを受け取ります...


7

あなたはそのためにreモジュールを使うことができます:

>>> import re
>>> re.compile(".*AAA(.*)ZZZ.*").match("gfgfdAAA1234ZZZuijjk").groups()
('1234,)

5

sedを使用すると、文字列を使用して次のようなことを実行できます。

echo "$STRING" | sed -e "s|.*AAA\(.*\)ZZZ.*|\1|"

その結果、1234が返されます。

re.sub同じ正規表現を使用して、関数でも同じことができます。

>>> re.sub(r'.*AAA(.*)ZZZ.*', r'\1', 'gfgfdAAA1234ZZZuijjk')
'1234'

基本的なsedでは、キャプチャグループはで表されます\(..\)が、Pythonではで表され(..)ます。


5

Pythonではfindall、正規表現(re)モジュールのメソッドを使用して部分文字列形式の文字列を抽出できます。

>>> import re
>>> s = 'gfgfdAAA1234ZZZuijjk'
>>> ss = re.findall('AAA(.+)ZZZ', s)
>>> print ss
['1234']

4

コード内でこの関数を使用して最初の部分文字列を見つけることができます(文字インデックスによって)。また、部分文字列の後に何があるかを見つけることができます。

def FindSubString(strText, strSubString, Offset=None):
    try:
        Start = strText.find(strSubString)
        if Start == -1:
            return -1 # Not Found
        else:
            if Offset == None:
                Result = strText[Start+len(strSubString):]
            elif Offset == 0:
                return Start
            else:
                AfterSubString = Start+len(strSubString)
                Result = strText[AfterSubString:AfterSubString + int(Offset)]
            return Result
    except:
        return -1

# Example:

Text = "Thanks for contributing an answer to Stack Overflow!"
subText = "to"

print("Start of first substring in a text:")
start = FindSubString(Text, subText, 0)
print(start); print("")

print("Exact substring in a text:")
print(Text[start:start+len(subText)]); print("")

print("What is after substring \"%s\"?" %(subText))
print(FindSubString(Text, subText))

# Your answer:

Text = "gfgfdAAA1234ZZZuijjk"
subText1 = "AAA"
subText2 = "ZZZ"

AfterText1 = FindSubString(Text, subText1, 0) + len(subText1)
BeforText2 = FindSubString(Text, subText2, 0) 

print("\nYour answer:\n%s" %(Text[AfterText1:BeforText2]))

3
>>> s = '/tmp/10508.constantstring'
>>> s.split('/tmp/')[1].split('constantstring')[0].strip('.')


2

念のため、誰かが私と同じことをしなければならないでしょう。括弧内のすべてを一行に抽出する必要がありました。たとえば、「米国大統領(バラクオバマ)と出会った...」のような行があり、「バラクオバマ」だけを取得したい場合、これが解決策です。

regex = '.*\((.*?)\).*'
matches = re.search(regex, line)
line = matches.group(1) + '\n'

つまり、括弧をブロックする必要があります slash \記号で。Pythonよりも正規表現の問題ですが。

また、場合によっては、正規表現の定義の前に「r」記号が表示されることがあります。r接頭辞がない場合は、Cのようにエスケープ文字を使用する必要があります。これについては、以下で詳しく説明します。


2

PyParsingの使用

import pyparsing as pp

word = pp.Word(pp.alphanums)

s = 'gfgfdAAA1234ZZZuijjk'
rule = pp.nestedExpr('AAA', 'ZZZ')
for match in rule.searchString(s):
    print(match)

これにより、

[['1234']]


0

これは、最初の部分文字列に2番目の部分文字列が含まれるシナリオも考慮した正規表現なしのソリューションです。この関数は、2番目のマーカーが最初のマーカーの後にある場合にのみ部分文字列を検索します。

def find_substring(string, start, end):
    len_until_end_of_first_match = string.find(start) + len(start)
    after_start = string[len_until_end_of_first_match:]
    return string[string.find(start) + len(start):len_until_end_of_first_match + after_start.find(end)]

0

これを行う別の方法は、リストを使用することです(探している部分文字列が数字のみで構成されていると仮定します)。

string = 'gfgfdAAA1234ZZZuijjk'
numbersList = ['0', '1', '2', '3', '4', '5', '6', '7', '8', '9']
output = []

for char in string:
    if char in numbersList: output.append(char)

print(f"output: {''.join(output)}")
### output: 1234

-1

一致しない場合に他の文字列を返す1つのライナー。編集:改良版はnext関数を使用し、"not-found"必要に応じて他のものに置き換えます:

import re
res = next( (m.group(1) for m in [re.search("AAA(.*?)ZZZ", "gfgfdAAA1234ZZZuijjk" ),] if m), "not-found" )

これを行う私の他の方法は、最適ではありませんが、正規表現を2回使用しますが、それでも短い方法は見つかりませんでした。

import re
res = ( ( re.search("AAA(.*?)ZZZ", "gfgfdAAA1234ZZZuijjk") or re.search("()","") ).group(1) )
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.