Làm thế nào để trích xuất chuỗi con giữa hai điểm đánh dấu?


334

Giả sử tôi có một chuỗi 'gfgfdAAA1234ZZZuijjk'và tôi chỉ muốn trích xuất '1234'một phần.

Tôi chỉ biết những gì sẽ là một vài nhân vật trực tiếp trước AAA, và sau ZZZphần tôi quan tâm 1234.

Với sednó có thể làm một cái gì đó như thế này với một chuỗi:

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

Và điều này sẽ cho tôi 1234kết quả.

Làm thế nào để làm điều tương tự trong Python?

Câu trả lời:


585

Sử dụng biểu thức chính quy - tài liệu để tham khảo thêm

import re

text = 'gfgfdAAA1234ZZZuijjk'

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

# found: 1234

hoặc là:

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
Giải pháp thứ hai là tốt hơn, nếu mô hình phù hợp với hầu hết thời gian, bởi vì nó dễ dàng hơn để yêu cầu sự tha thứ hơn là sự cho phép. .
Bengt

7
Không lập chỉ mục bắt đầu từ 0? Vì vậy, bạn sẽ cần phải sử dụng nhóm (0) thay vì nhóm (1)?
Alexander

22
@Alexander, không, nhóm (0) sẽ trả về chuỗi phù hợp đầy đủ: AAA1234ZZZ và nhóm (1) sẽ chỉ trả lại các ký tự được khớp bởi nhóm thứ nhất: 1234
Yurii K

1
@Bengt: Tại sao vậy? Giải pháp đầu tiên có vẻ khá đơn giản đối với tôi và nó có ít dòng mã hơn.
HelloGoodbye

5
Trong biểu thức này là? sửa đổi + thành không tham lam, tức là. nó sẽ khớp với bất kỳ số lần nào từ 1 trở lên nhưng càng ít càng tốt, chỉ mở rộng khi cần thiết. không có?, nhóm đầu tiên sẽ khớp với gfgfAAA2ZZZkeAAA43ZZZonife là 2ZZZkeAAA43, nhưng với? nó sẽ chỉ khớp với 2, sau đó tìm kiếm nhiều (hoặc loại bỏ nó và tìm kiếm lại) sẽ khớp với 43.
Dom

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

Sau đó, bạn có thể sử dụng regexps với mô đun re, nếu bạn muốn, nhưng điều đó không cần thiết trong trường hợp của bạn.


9
Câu hỏi dường như ngụ ý rằng văn bản đầu vào sẽ luôn chứa cả "AAA" và "ZZZ". Nếu đây không phải là trường hợp, câu trả lời của bạn thất bại khủng khiếp (ý tôi là nó trả về một cái gì đó hoàn toàn sai thay vì một chuỗi rỗng hoặc ném một ngoại lệ; hãy nghĩ "xin chào" như chuỗi đầu vào).
tzot

@ user225312 rePhương thức này không nhanh hơn phải không?
bối

1
Bỏ phiếu, nhưng tôi sẽ sử dụng "x = 'AAA'; s.find (x) + len (x)" thay vì "s.find ('AAA') + 3" để duy trì.
Alex

1
Nếu bất kỳ mã thông báo nào không thể được tìm thấy trong s, s.findsẽ quay trở lại -1. toán tử cắt s[begin:end] sẽ chấp nhận nó là chỉ mục hợp lệ và trả về chuỗi con không mong muốn.
ribamar

@ bối rối tìm thấy nhanh hơn nhiều so với re stackoverflow.com/questions/4901523/
Kẻ

63

biểu hiện thông thường

import re

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

Các nguyên tắc trên sẽ thất bại với AttributeErrornếu không có "AAA" và "ZZZ" trongyour_text

phương thức chuỗi

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

Ở trên sẽ trả về một chuỗi trống nếu "AAA" hoặc "ZZZ" không tồn tại your_text.

Thử thách PS Python?


6
Câu trả lời này có lẽ xứng đáng được nhiều phiếu hơn. Phương thức chuỗi là cách mạnh mẽ nhất. Nó không cần thử / ngoại trừ.
ChaimG

... tốt, mặc dù hạn chế. phân vùng không dựa trên regex, vì vậy nó chỉ hoạt động trong trường hợp này vì chuỗi tìm kiếm bị giới hạn bởi các chữ cố định
GreenAsJade

Tuyệt vời, cảm ơn rất nhiều! - điều này hoạt động cho chuỗi và không yêu cầu regex
Alex

Chúa ơi! thực sự, phân vùng! Cảm ơn nhiều!
Andrey Wal

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

1
AttributeError: 'NoneType' object has no attribute 'groups'- nếu không có AAA, ZZZ trong chuỗi ...
eumiro

12

Ngạc nhiên vì không ai đề cập đến điều này, đây là phiên bản nhanh của tôi cho các tập lệnh một lần:

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

@ user1810100 đã đề cập về cơ bản là gần 5 năm cho đến ngày trước khi bạn đăng bài này ...
John

10

bạn có thể làm chỉ bằng một dòng mã

>>> import re

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

>>> ['1234']

kết quả sẽ nhận được danh sách ...


7

Bạn có thể sử dụng mô-đun re cho điều đó:

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

5

Với sed, có thể làm một cái gì đó như thế này bằng một chuỗi:

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

Và điều này sẽ cho tôi 1234 như là kết quả.

Bạn có thể làm tương tự với re.subchức năng sử dụng cùng một biểu thức chính quy.

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

Trong sed cơ bản, nhóm bắt được đại diện bởi \(..\), nhưng trong python nó được đại diện bởi (..).


5

Trong python, trích xuất chuỗi biểu mẫu chuỗi con có thể được thực hiện bằng findallphương thức trong remô đun biểu thức chính quy ( ).

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

4

Bạn có thể tìm thấy chuỗi con đầu tiên có chức năng này trong mã của bạn (theo chỉ mục ký tự). Ngoài ra, bạn có thể tìm thấy những gì sau một chuỗi con.

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

Tặng

string

2

Chỉ trong trường hợp ai đó sẽ phải làm điều tương tự mà tôi đã làm. Tôi đã phải trích xuất mọi thứ trong ngoặc đơn trong một dòng. Ví dụ: nếu tôi có một dòng như 'Tổng thống Mỹ (Barack Obama) đã gặp ...' và tôi chỉ muốn nhận được 'Barack Obama' thì đây là giải pháp:

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

Tức là bạn cần chặn dấu ngoặc đơn với slash \ dấu hiệu. Mặc dù đó là một vấn đề về các biểu thức chính quy hơn mà Python.

Ngoài ra, trong một số trường hợp, bạn có thể thấy các ký hiệu 'r' trước khi xác định regex. Nếu không có tiền tố r, bạn cần sử dụng các ký tự thoát như trong C. Dưới đây là thảo luận thêm về điều đó.


2

Sử dụng 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)

mang lại:

[['1234']]


0

Đây là một giải pháp không có regex cũng chiếm các tình huống trong đó chuỗi con thứ nhất chứa chuỗi con thứ hai. Hàm này sẽ chỉ tìm một chuỗi con nếu điểm đánh dấu thứ hai nằm sau điểm đánh dấu thứ nhất.

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

Một cách khác để làm điều đó là sử dụng danh sách (giả sử chuỗi con bạn đang tìm kiếm chỉ được làm bằng số):

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

Một lớp lót trả về chuỗi khác nếu không có kết quả khớp. Chỉnh sửa: phiên bản cải tiến sử dụng nextchức năng, thay thế "not-found"bằng thứ khác nếu cần:

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

Phương pháp khác của tôi để làm điều này, ít tối ưu hơn, sử dụng regex lần thứ 2, vẫn không tìm thấy cách nào ngắn hơn:

import re
res = ( ( re.search("AAA(.*?)ZZZ", "gfgfdAAA1234ZZZuijjk") or re.search("()","") ).group(1) )
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.