İki işaretleyici arasında alt dize nasıl çıkarılır?


335

Diyelim ki bir dizem var 'gfgfdAAA1234ZZZuijjk've sadece '1234'parçayı çıkarmak istiyorum .

Sadece daha önce AAAve ZZZilgilendiğim bölümden sonra birkaç karakterin ne olacağını biliyorum 1234.

İle sedkendisine bir dize ile böyle bir şey yapmak mümkündür:

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

Bu da bana 1234sonuç verecek .

Python'da aynı şey nasıl yapılır?

Yanıtlar:


588

Normal ifadeleri kullanma - daha fazla başvuru için belgeler

import re

text = 'gfgfdAAA1234ZZZuijjk'

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

# found: 1234

veya:

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
Desen çoğu zaman eşleşirse ikinci çözüm daha iyidir, çünkü izin vermekten af ​​dilemek daha kolaydır. .
Bengt

7
Dizin oluşturma 0'dan başlamıyor mu? Yani grup (1) yerine grup (0) kullanmanız gerekir mi?
Alexander

22
@Alexander, hayır, grup (0) tam eşleşen dizeyi döndürür: AAA1234ZZZ ve grup (1) yalnızca ilk grupla eşleşen karakterleri döndürür: 1234
Yurii K

1
@Bengt: Neden böyle? İlk çözüm benim için oldukça basit görünüyor ve daha az kod satırı var.
HelloGoodbye

5
Bu ifadede? + işaretini açgözlü olmayacak şekilde değiştirir, yani. 1'den yukarı ancak mümkün olduğu kadar az sayıda, yalnızca gerektiği kadar genişleyerek istediğiniz sayıda eşleşir. ? olmadan, ilk grup gfgfAAA2ZZZkeAAA43ZZZonife ile 2ZZZkeAAA43 olarak eşleşir, ancak? sadece 2 ile eşleşir, daha sonra çoklu aramak (veya sıyırmak ve tekrar aramak) 43 ile eşleşir.
Dom

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

Daha sonra, isterseniz re modülü ile re modülünü de kullanabilirsiniz, ancak bu sizin durumunuzda gerekli değildir.


9
Soru, giriş metninin her zaman hem "AAA" hem de "ZZZ" içereceği anlamına geliyor. Bu durumda, cevabınız korkunç bir şekilde başarısız olur (yani boş bir dize veya bir istisna atmak yerine tamamen yanlış bir şey döndürür; giriş dizesi olarak "merhaba" düşünün).
tzot

@ user225312 reYöntem daha hızlı değil mi?
confused00

1
Oy verin, ancak sürdürülebilirlik için "s.find ('AAA') + 3" yerine "x = 'AAA'; s.find (x) + len (x)" kullanacağım.
Alex

1
Jetonlardan herhangi biri içinde bulunamazsa s, s.findgeri döner -1. dilimleme operatörü s[begin:end] bunu geçerli dizin olarak kabul eder ve istenmeyen alt dizeyi döndürür.
ribamar

@ confused00 find yeniden yapmaktan çok daha hızlı stackoverflow.com/questions/4901523/…
Claudiu Creanga

65

Düzenli ifade

import re

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

Yukarıdaki olduğu gibi AttributeError"AAA" ve "ZZZ" yoksayour_text

string yöntemleri

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

Yukarıda "AAA" veya "ZZZ" yoksa boş bir dize döndürülür your_text.

PS Python Mücadelesi?


6
Bu cevap muhtemelen daha fazla oyu hak ediyor. Dize yöntemi en sağlam yoldur. Bir deneme / hariç gerekmez.
ChaimG

... güzel, sınırlı da olsa. bölüm normal
ifadeye

Harika, çok teşekkürler! - Bu, dizeler için çalışır ve normal ifade gerektirmez
Alex

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

1
AttributeError: 'NoneType' object has no attribute 'groups'- Eğer dize AAA, ZZZ yoksa ...
eumiro

12

Kimsenin bir kerelik komut dosyaları için hızlı sürümüm olandan bahsetmediğini şaşırttı:

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

@ user1810100, bunu yayınlamadan önceki güne neredeyse tam olarak 5 yıl geçtiğini belirtti ...
John

10

sadece bir satır kod kullanarak yapabilirsiniz

>>> import re

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

>>> ['1234']

sonuç listesi alınacak ...


7

Bunun için re modülünü kullanabilirsiniz :

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

5

Sed ile böyle bir şey bir dize ile yapmak mümkündür:

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

Ve sonuç olarak bu bana 1234 verecek.

Aynı işlemleri olabilir re.subaynı regex kullanarak fonksiyonu.

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

Temel sed'de, yakalama grubu ile temsil edilir \(..\), ancak python ile temsil edilir (..).


5

Python'da, findallnormal ifade ( re) modülündeki yöntem kullanılarak alt dize form dizesinin ayıklanması yapılabilir .

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

4

Bu işleve sahip ilk alt dizeyi kodunuzda bulabilirsiniz (karakter dizinine göre). Ayrıca, bir alt dize sonra ne olduğunu bulabilirsiniz.

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('.')

3
text = 'I want to find a string between two substrings'
left = 'find a '
right = 'between two'

print(text[text.index(left)+len(left):text.index(right)])

verir

string

2

Birisi benim yaptığım şeyi yapmak zorunda kalırsa. Parantez içindeki her şeyi bir satırda çıkarmak zorunda kaldım. Örneğin, 'ABD başkanı (Barack Obama) ile bir araya geldi ...' gibi bir hattım varsa ve sadece 'Barack Obama' almak istiyorum bu çözüm:

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

Yani parantezin slash \işaretli olması gerekir. Python daha düzenli ifadeler hakkında bir sorun olsa da.

Ayrıca, bazı durumlarda normal ifade tanımından önce 'r' sembolleri görebilirsiniz. R öneki yoksa, C'deki gibi kaçış karakterleri kullanmanız gerekir. İşte bununla ilgili daha fazla tartışma.


2

PyParsing'i kullanma

import pyparsing as pp

word = pp.Word(pp.alphanums)

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

hangi verir:

[['1234']]


0

Aşağıda, ilk alt dizenin ikinci alt dizeyi içerdiği senaryoları da hesaba katan regex içermeyen bir çözüm bulunmaktadır. Bu işlev, yalnızca ikinci işaretçi ilk işaretleyiciden sonraysa bir alt dize bulur.

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

Bunu yapmanın başka bir yolu da listeleri kullanmaktır (aradığınız alt dizenin yalnızca sayılardan oluştuğunu varsayalım):

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

Eşleşme yoksa diğer dizeyi döndüren bir gömlek. Düzenleme: geliştirilmiş sürüm nextişlevi kullanır , "not-found"gerekirse başka bir şeyle değiştirin :

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

Bunu yapmak için diğer yöntemim, daha az optimal, regex 2. kez kullanıyor, yine de daha kısa bir yol bulamadı:

import re
res = ( ( re.search("AAA(.*?)ZZZ", "gfgfdAAA1234ZZZuijjk") or re.search("()","") ).group(1) )
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.