วิธีการแยก substring ระหว่างสองเครื่องหมาย?


335

สมมติว่าฉันมีสตริง'gfgfdAAA1234ZZZuijjk'และฉันต้องการแยก'1234'ส่วน

ฉันเพียงรู้สิ่งที่จะเป็นตัวอักษรไม่กี่โดยตรงก่อนAAAและหลังจากที่เป็นส่วนหนึ่งที่ผมสนใจในZZZ1234

ด้วยsedเป็นไปได้ที่จะทำอะไรเช่นนี้กับสตริง:

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

และนี่จะ1234เป็นผลให้ฉัน

วิธีการทำสิ่งเดียวกันใน Python?

คำตอบ:


588

การใช้นิพจน์ทั่วไป - เอกสารประกอบเพื่อการอ้างอิงเพิ่มเติม

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
วิธีที่สองจะดีกว่าหากรูปแบบการจับคู่ส่วนใหญ่เพราะมันง่ายกว่าที่จะขอการอภัยมากกว่าการอนุญาต .
Bengt

7
ดัชนีเริ่มต้นที่ 0 หรือไม่? ดังนั้นคุณจะต้องใช้กลุ่ม (0) แทนกลุ่ม (1)?
Alexander

22
@Alexander ไม่กลุ่ม (0) จะส่งคืนสตริงที่จับคู่แบบเต็ม: AAA1234ZZZ และกลุ่ม (1) จะส่งกลับเฉพาะอักขระที่ตรงกับกลุ่มแรก: 1234
Yurii K

1
@Bengt: ทำไมล่ะ โซลูชันแรกนั้นดูเรียบง่ายสำหรับฉันและมีโค้ดน้อยกว่า
HelloGoodbye

5
ในการแสดงออกนี้ แก้ไขเครื่องหมาย + เป็นแบบไม่โลภเช่น มันจะจับคู่กับจำนวนครั้งจาก 1 ขึ้นไป แต่น้อยที่สุดเท่าที่เป็นไปได้เพียงขยายเท่าที่จำเป็น หากไม่มีกลุ่มแรกจะจับคู่ gfgfAAA2ZZZkeAAA43ZZZonife เป็น 2ZZZkeAAA43 แต่ใช้? มันจะตรงกับ 2 เท่านั้นแล้วค้นหาหลาย ๆ (หรือถ้ามีการแยกออกและค้นหาอีกครั้ง) จะตรงกับ 43
Dom

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

จากนั้นคุณสามารถใช้ regexps กับโมดูลใหม่ได้เช่นกันหากคุณต้องการ แต่ไม่จำเป็นในกรณีของคุณ


9
คำถามดูเหมือนจะบอกเป็นนัยว่าข้อความที่ป้อนจะมีทั้ง "AAA" และ "ZZZ" เสมอ หากไม่ใช่ในกรณีนี้คำตอบของคุณจะล้มเหลวอย่างน่ากลัว (โดยที่ฉันหมายความว่ามันส่งคืนสิ่งผิดปกติอย่างสมบูรณ์แทนที่จะเป็นสตริงว่างหรือโยนข้อยกเว้นให้คิดว่า "hello there" เป็นสตริงอินพุต)
tzot

@ user225312 เป็นreวิธีที่ไม่เร็วขึ้นหรือไม่
สับสน

1
โหวต แต่ฉันจะใช้ "x = 'AAA'; s.find (x) + len (x)" แทนที่จะเป็น "s.find ('AAA') + 3" สำหรับการบำรุงรักษา
อเล็กซ์

1
ถ้าใด ๆ ของสัญญาณไม่สามารถพบได้ในs, จะกลับมาs.find -1ตัวดำเนินการแบ่งส่วนs[begin:end] จะยอมรับว่าเป็นดัชนีที่ถูกต้องและส่งคืนสตริงย่อยที่ไม่ต้องการ
ribamar

@ สับสน
00 find

65

การแสดงออกปกติ

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 Challenge ไหม?


6
คำตอบนี้อาจสมควรได้รับการโหวตมากขึ้น วิธีสตริงเป็นวิธีที่แข็งแกร่งที่สุด มันไม่จำเป็นต้องลอง / ยกเว้น
ChaimG

... ดี แต่มีข้อ จำกัด พาร์ติชันไม่ได้ขึ้นอยู่กับ regex ดังนั้นจึงสามารถใช้งานได้ในอินสแตนซ์นี้เท่านั้นเนื่องจากสตริงการค้นหาถูกล้อมรอบด้วยตัวอักษรคงที่
GreenAsJade

เยี่ยมมากขอบคุณมาก! - ใช้งานได้กับสตริงและไม่ต้องใช้ regex
Alex


12

แปลกใจที่ไม่มีใครพูดถึงสิ่งนี้ซึ่งเป็นเวอร์ชันย่อของฉันสำหรับสคริปต์แบบครั้งเดียว:

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

@ user1810100 ได้กล่าวถึงหลักแล้วว่าเกือบ 5 ปีก่อนที่คุณจะโพสต์ข้อความนี้ ...
John

10

คุณสามารถทำได้โดยใช้โค้ดเพียงบรรทัดเดียว

>>> import re

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

>>> ['1234']

ผลจะได้รับรายการ ...



5

ด้วย sed คุณสามารถทำสิ่งนี้กับสตริง:

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

และนี่จะให้ผล 1234 กับฉัน

คุณสามารถทำเช่นเดียวกันกับre.subฟังก์ชันโดยใช้ regex เดียวกัน

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

ในขั้นพื้นฐาน sed กลุ่มจับโดยมีตัวแทนแต่ในหลามมันเป็นตัวแทนจาก\(..\)(..)


5

ในไพ ธ อนการแยกสตริงสตริงย่อยสามารถทำได้โดยใช้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]))



2

ในกรณีที่บางคนจะต้องทำสิ่งเดียวกันกับที่ฉันทำ ฉันต้องแยกทุกอย่างไว้ในวงเล็บเป็นเส้น ตัวอย่างเช่นถ้าฉันมีบรรทัดเช่น 'ประธานาธิบดีสหรัฐ (บารัคโอบามา) พบกับ ... ' และฉันต้องการได้รับเพียง 'บารักโอบา' นี่เป็นวิธีแก้ปัญหา:

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

นั่นคือคุณต้องปิดกั้นวงเล็บด้วยslash \เครื่องหมาย แม้ว่ามันจะเป็นปัญหาเกี่ยวกับการแสดงออกปกติมากขึ้นว่างูหลาม

นอกจากนี้ในบางกรณีคุณอาจเห็นสัญลักษณ์ 'r' ก่อนกำหนด regex หากไม่มีคำนำหน้า r คุณจำเป็นต้องใช้อักขระเลี่ยงเหมือนในซีต่อไปนี้คือการอภิปรายเพิ่มเติมเกี่ยวกับเรื่องนั้น


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

นี่เป็นวิธีการแก้ปัญหาโดยไม่ต้อง regex ที่บัญชีสำหรับสถานการณ์ที่สตริงย่อยแรกมีสตริงย่อยที่สอง ฟังก์ชันนี้จะค้นหาสตริงย่อยเฉพาะถ้าตัวทำเครื่องหมายที่สองอยู่หลังตัวทำเครื่องหมายแรก

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

หนึ่ง liners ที่ส่งคืนสตริงอื่นหากไม่มีการจับคู่ แก้ไข: รุ่นที่ปรับปรุงใช้nextฟังก์ชั่นแทนที่"not-found"ด้วยอย่างอื่นถ้าจำเป็น:

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

วิธีอื่นของฉันในการทำสิ่งนี้ให้เหมาะสมน้อยกว่าใช้ regex ครั้งที่ 2 ยังไม่พบวิธีที่สั้นกว่า:

import re
res = ( ( re.search("AAA(.*?)ZZZ", "gfgfdAAA1234ZZZuijjk") or re.search("()","") ).group(1) )
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.