Bagaimana cara mengekstraksi substring antara dua marker?


335

Katakanlah saya memiliki string 'gfgfdAAA1234ZZZuijjk'dan saya ingin mengekstrak '1234'bagiannya saja.

Saya hanya tahu apa yang akan menjadi beberapa karakter secara langsung sebelumnya AAA, dan setelah ZZZbagian saya tertarik 1234.

Dengan seddimungkinkan untuk melakukan sesuatu seperti ini dengan string:

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

Dan ini akan memberi saya 1234hasilnya.

Bagaimana melakukan hal yang sama dengan Python?

Jawaban:


588

Menggunakan ekspresi reguler - dokumentasi untuk referensi lebih lanjut

import re

text = 'gfgfdAAA1234ZZZuijjk'

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

# found: 1234

atau:

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
Solusi kedua lebih baik, jika polanya cocok sebagian besar waktu, karena lebih mudah untuk meminta pengampunan daripada izin. .
Bengt

7
Bukankah pengindeksan dimulai pada 0? Jadi, Anda perlu menggunakan grup (0), bukan grup (1)?
Alexander

22
@Alexander, tidak, grup (0) akan mengembalikan string yang cocok sepenuhnya: AAA1234ZZZ, dan grup (1) hanya akan mengembalikan karakter yang cocok dengan grup pertama: 1234
Yurii K

1
@ Bangt: Mengapa begitu? Solusi pertama terlihat cukup sederhana bagi saya, dan memiliki lebih sedikit baris kode.
HelloGoodbye

5
Dalam ungkapan ini? memodifikasi + menjadi tidak rakus, yaitu. itu akan cocok beberapa kali dari 1 ke atas tetapi sesedikit mungkin, hanya berkembang seperlunya. tanpa ?, grup pertama akan mencocokkan gfgfAAA2ZZZkeAAA43ZZZonife sebagai 2ZZZkeAAA43, tetapi dengan? hanya cocok dengan 2, kemudian mencari beberapa (atau menghapusnya dan mencari lagi) akan cocok dengan 43.
Dom

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

Kemudian Anda dapat menggunakan regexps dengan modul re juga, jika Anda mau, tetapi itu tidak perlu dalam kasus Anda.


9
Pertanyaannya sepertinya menyiratkan bahwa teks input akan selalu mengandung "AAA" dan "ZZZ". Jika ini tidak terjadi, jawaban Anda gagal mengerikan (maksud saya mengembalikan sesuatu yang benar-benar salah, bukan string kosong atau melempar pengecualian; pikirkan "halo di sana" sebagai string input).
tzot

@ user225312 Apakah remetode ini tidak lebih cepat?
confused00

1
Voteup, tapi saya akan menggunakan "x = 'AAA'; s.find (x) + len (x)" sebagai ganti "s.find ('AAA') + 3" untuk pemeliharaan.
Alex

1
Jika salah satu token tidak dapat ditemukan di s, s.findakan kembali -1. operator pengiris s[begin:end] akan menerimanya sebagai indeks yang valid, dan mengembalikan substring yang tidak diinginkan.
ribamar

@ confused00 find jauh lebih cepat daripada re stackoverflow.com/questions/4901523/...
Claudiu Creanga

65

ekspresi reguler

import re

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

As-is di atas akan gagal dengan AttributeErrorjika tidak ada "AAA" dan "ZZZ" diyour_text

metode string

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

Di atas akan mengembalikan string kosong jika "AAA" atau "ZZZ" tidak ada di your_text.

Tantangan PS Python?


6
Jawaban ini mungkin layak mendapat lebih banyak suara. Metode string adalah cara yang paling kuat. Tidak perlu dicoba / kecuali.
ChaimG

... bagus, meskipun terbatas. partisi bukan berbasis regex, jadi itu hanya berfungsi dalam hal ini karena string pencarian dibatasi oleh literal tetap
GreenAsJade

Hebat, terima kasih banyak! - ini berfungsi untuk string dan tidak memerlukan regex
Alex

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

1
AttributeError: 'NoneType' object has no attribute 'groups'- jika tidak ada AAA, ZZZ dalam string ...
eumiro

12

Terkejut bahwa tidak ada yang menyebutkan ini yang merupakan versi cepat saya untuk skrip satu kali:

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

@ user1810100 menyebutkan pada dasarnya bahwa hampir tepat 5 tahun sebelum Anda memposting ini ...
John

10

Anda dapat melakukannya hanya dengan menggunakan satu baris kode

>>> import re

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

>>> ['1234']

hasilnya akan menerima daftar ...


8

Anda dapat menggunakan modul re untuk itu:

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

5

Dengan sed, dimungkinkan untuk melakukan sesuatu seperti ini dengan sebuah string:

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

Dan ini akan memberi saya 1234 sebagai hasilnya.

Anda bisa melakukan hal yang sama dengan re.subfungsi menggunakan regex yang sama.

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

Dalam sed dasar, grup menangkap diwakili oleh \(..\), tetapi dalam python diwakili oleh (..).


5

Dalam python, mengekstraksi string bentuk substring dapat dilakukan dengan menggunakan findallmetode dalam regular expression ( re) module.

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

4

Anda dapat menemukan substring pertama dengan fungsi ini dalam kode Anda (berdasarkan indeks karakter). Juga, Anda dapat menemukan apa yang setelah substring.

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

Memberi

string

2

Kalau-kalau ada yang harus melakukan hal yang sama yang saya lakukan. Saya harus mengekstrak semua yang ada di dalam tanda kurung. Misalnya, jika saya memiliki garis seperti 'Presiden AS (Barack Obama) bertemu dengan ...' dan saya hanya ingin mendapatkan 'Barack Obama' ini adalah solusi:

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

Yaitu Anda perlu memblokir tanda kurung dengan slash \tanda. Padahal itu adalah masalah tentang ekspresi yang lebih teratur dari Python.

Juga, dalam beberapa kasus, Anda mungkin melihat simbol 'r' sebelum definisi regex. Jika tidak ada awalan r, Anda perlu menggunakan karakter escape seperti di C. Ini adalah diskusi lebih lanjut tentang itu.


2

Menggunakan 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)

yang menghasilkan:

[['1234']]


0

Berikut adalah solusi tanpa regex yang juga memperhitungkan skenario di mana substring pertama berisi substring kedua. Fungsi ini hanya akan menemukan substring jika penanda kedua adalah setelah penanda pertama.

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

Cara lain untuk melakukannya adalah menggunakan daftar (seandainya substring yang Anda cari terbuat dari angka saja):

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

Satu liner yang mengembalikan string lain jika tidak ada kecocokan. Sunting: versi yang ditingkatkan menggunakan nextfungsi, ganti "not-found"dengan yang lain jika diperlukan:

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

Metode saya yang lain untuk melakukan ini, kurang optimal, menggunakan regex 2nd time, masih belum menemukan cara yang lebih pendek:

import re
res = ( ( re.search("AAA(.*?)ZZZ", "gfgfdAAA1234ZZZuijjk") or re.search("()","") ).group(1) )
Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.