2012-07-11 32 views
7

Tôi đang cố xóa một số nội dung khỏi khối văn bản bằng regex. Tôi có tất cả các mẫu của tôi đã sẵn sàng, nhưng tôi dường như không thể loại bỏ hai (hoặc nhiều hơn) chồng lên nhau.Kết hợp nhiều thay thế regex

Ví dụ:

import re 

r1 = r'I am' 
r2 = r'am foo' 

text = 'I am foo' 

re.sub(r1, '', text) # Returns ' foo' 
re.sub(r2, '', text) # Returns 'I ' 

Làm thế nào để thay thế cả hai lần xuất hiện đồng thời và kết thúc với một chuỗi rỗng?


tôi đã kết thúc bằng một phiên bản sửa đổi nhẹ của Ned Batchelder's answer:

def clean(self, text): 
    mask = bytearray(len(text)) 

    for pattern in patterns: 
    for match in re.finditer(pattern, text): 
     r = range(match.start(), match.end()) 

     mask[r] = 'x' * len(r) 

    return ''.join(character for character, bit in zip(text, mask) if not bit) 

Trả lời

12

Bạn không thể thực hiện việc này với các cuộc gọi re.sub liên tiếp như bạn đã hiển thị. Bạn có thể sử dụng re.finditer để tìm tất cả. Mỗi trận đấu sẽ cung cấp cho bạn đối tượng đối sánh, trong đó có các thuộc tính .start.end cho biết vị trí của chúng. Bạn có thể thu thập tất cả những thứ đó lại với nhau và sau đó xóa ký tự ở cuối.

Ở đây tôi sử dụng bytearray làm chuỗi có thể thay đổi được, được sử dụng làm mặt nạ. Nó được khởi tạo thành 0 byte và tôi đánh dấu bằng một 'x' tất cả các byte khớp với bất kỳ regex nào. Sau đó, tôi sử dụng mặt nạ bit để chọn nhân vật để giữ trong chuỗi ban đầu, và xây dựng một chuỗi mới với những chữ số chưa từng có:

bits = bytearray(len(text)) 
for pat in patterns: 
    for m in re.finditer(pat, text): 
     bits[m.start():m.end()] = 'x' * (m.end()-m.start()) 
new_string = ''.join(c for c,bit in zip(text, bits) if not bit) 
+0

Tôi chưa bao giờ nghĩ đến thuộc tính 'bắt đầu' và' cuối' cho đối tượng khớp. Tôi chắc chắn rằng điều này sẽ làm việc, vì vậy cảm ơn! – Blender

+1

Câu trả lời hay! Tôi đã thêm '()' vào 'bắt đầu' và' kết thúc', vì đây là các phương thức chứ không phải thuộc tính. – georg

+0

@ thg435: cảm ơn, tôi nên thử nghiệm nó! :) –

2

Không phải là một Downer, nhưng câu trả lời ngắn gọn là tôi khá chắc chắn bạn không thể. Bạn có thể thay đổi regex của mình để không yêu cầu trùng lặp không?

Nếu bạn vẫn muốn thực hiện việc này, tôi sẽ cố gắng theo dõi các chỉ số bắt đầu và dừng bắt đầu và dừng của mỗi kết quả được thực hiện trên chuỗi gốc. Sau đó đi qua chuỗi và chỉ giữ các ký tự không nằm trong bất kỳ phạm vi xóa nào?

1

Khá hiệu quả quá là một giải pháp đến từ ... Perl kết hợp regexps trong một:

# aptitude install regexp-assemble 
$ regexp-assemble 
I am 
I am foo 
Ctrl + D 
I am(?: foo)? 

regexp-lắp ráp mất tất cả các biến thể của regexps hoặc chuỗi bạn muốn kết hợp và sau đó kết hợp chúng trong một. Và có nó làm thay đổi vấn đề ban đầu với nhau vì nó không phải là về phù hợp chồng chéo regexp nữa, nhưng kết hợp regexp cho một trận đấu

Và Sau đó, bạn có thể sử dụng nó trong mã của bạn:

$ python 
Python 2.7.3 (default, Aug 1 2012, 05:14:39) 
[GCC 4.6.3] on linux2 
Type "help", "copyright", "credits" or "license" for more information. 
>>> import re 
>>> re.sub("I am foo","I am(?: foo)?","") 
'' 

Một cảng Regexp :: Lắp ráp trong python sẽ được tốt đẹp :)

+0

Lệnh này là 'aptitude install libregexp-assemble-perl'. Tôi không thể nhanh chóng tìm thấy bất kỳ dấu vết của một gói trước đó với tên bạn chỉ ra, nhưng có thể bạn đang trên một bản phân phối khác nhau; điều này là dành cho Debian ổn định. – tripleee

+0

Ngoài ra, trong các phiên bản cũ của gói, bản demo chỉ có trong '/ usr/share/doc/libregexp-assemble-perl/example/assemble.gz' - Tôi muốn điều này trên một hộp' squeeze', nơi tập lệnh không được cài đặt với tên bạn cho biết. – tripleee

1

Dưới đây là giải pháp thay thế để lọc các chuỗi đang bay bằng cách sử dụng itertools.compress trên văn bản với trình chọn bộ chọn. Bộ chọn trả về True nếu ký tự phải được giữ. selector_for_patterns tạo một bộ chọn cho mọi mẫu. Bộ chọn được kết hợp với tất cả các chức năng (chỉ khi tất cả các mô hình muốn giữ một ký tự nó phải có trong chuỗi kết quả).

import itertools 
import re 

def selector_for_pattern(text, pattern): 
    i = 0 
    for m in re.finditer(pattern, text): 
     for _ in xrange(i, m.start()): 
      yield True 
     for _ in xrange(m.start(), m.end()): 
      yield False 
     i = m.end() 
    for _ in xrange(i, len(text)): 
     yield True 

def clean(text, patterns): 
    gen = [selector_for_pattern(text, pattern) for pattern in patterns] 
    selector = itertools.imap(all, itertools.izip(* gen)) 
    return "".join(itertools.compress(text, selector)) 
Các vấn đề liên quan