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?

miernik
sumber

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
eumiro
sumber
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.

Lennart Regebro
sumber
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?

tzot
sumber
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)
inframerah
sumber
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'
Rambut Panjang Paman
sumber
@ 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 ...

Mahesh Gupta
sumber
8

Anda dapat menggunakan modul re untuk itu:

>>> import re
>>> re.compile(".*AAA(.*)ZZZ.*").match("gfgfdAAA1234ZZZuijjk").groups()
('1234,)
andreypopp
sumber
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 (..).

Avinash Raj
sumber
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']
rashok
sumber
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]))
Saeed Zahedian Abroodi
sumber
3
>>> s = '/tmp/10508.constantstring'
>>> s.split('/tmp/')[1].split('constantstring')[0].strip('.')
pengguna1810100
sumber
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
Fernando Wittmann
sumber
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.

Denis Kutlubaev
sumber
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']]

Raphael
sumber
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)]
Roymunson
sumber
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
Julio Souto
sumber
-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) )
MaxLZ
sumber