2016-08-16 11 views
5

Trước hết câu hỏi được gắn thẻ với Pythonregex nhưng nó không thực sự gắn liền với chúng - một câu trả lời có thể ở mức cao.Tách chuỗi bởi sự xuất hiện đầu tiên từ một tập hợp các dấu phân cách bằng Python và regex

Hiện tại tôi đang tách một chuỗi có nhiều dấu phân tách bằng mẫu sau. Có rất nhiều bằng chứng thực sự khoanh vùng nhiều hơn và họ rất phức tạp hơn, nhưng chúng ta hãy giữ cho nó đơn giản và hạn chế chúng để 2 nhân vật - #*:

parts = re.split('#|*', string)

Những cách tiếp cận như vậy một chuỗi aaa#bbb*ccc#ddd được chia cho 4 chuỗi con aaa, bbb , ccc, ddd. Nhưng nó được yêu cầu phải phân tách bằng dấu phân tách xuất hiện đầu tiên trong chuỗi hoặc theo dấu phân cách thường xuyên nhất trong chuỗi. aaa#bbb*ccc#ddd phải được chia thành aaa, bbb*ccc, dddaaa*bbb#ccc*ddd phải được chia thành aaa, bbb#ccc, ddd.

Tôi biết một cách đơn giản để đạt được điều đó - để tìm dấu phân cách nào xảy ra trước tiên hoặc thường xuyên nhất trong chuỗi và sau đó chia tách với dấu phân tách đơn đó. Nhưng phương pháp này phải hiệu quả và tôi tự hỏi liệu có thể đạt được điều đó bằng một biểu thức chính quy duy nhất hay không. Câu hỏi chủ yếu là để tách với sự xuất hiện đầu tiên của tập hợp các dấu phân tách - đối với trường hợp phân cách thường xuyên nhất gần như chắc chắn nó sẽ được yêu cầu để tính toán số lần xuất hiện trước.

Cập nhật:

Câu hỏi đặt ra không yêu cầu chia theo xuất hiện đầu tiên hoặc delimiter thường gặp nhất cùng một lúc - bất kỳ các phương pháp này riêng lẻ nào sẽ là đủ. Tôi hiểu rằng việc phân tách bằng dấu phân cách thường xuyên nhất là không thể với regex mà không có sự xác định sơ bộ của dấu phân cách nhưng tôi nghĩ có khả năng sự phân tách bởi lần xuất hiện đầu tiên là có thể với regex và lookahead mà không cần chuẩn bị trước.

+4

Không có regex nào sẽ tìm thấy mẫu * thường xuyên nhất *. Bạn sẽ phải dựa vào các phương tiện ngôn ngữ khác. –

Trả lời

3

bắt buộc phải phân tách bằng dấu phân cách xảy ra đầu tiên trong chuỗi hoặc theo dấu phân cách thường xuyên nhất trong chuỗi.

Vì vậy, trước tiên bạn có thể tìm thấy tất cả các dấu phân tách và giữ chúng trong vùng chứa thích hợp với tần suất của chúng, sau đó tìm chuỗi phổ biến nhất và sau đó chia chuỗi của bạn dựa trên chúng. Để tìm các dấu phân cách, bạn cần tách chúng khỏi văn bản thuần túy dựa trên một tính năng cụ thể, ví dụ nếu chúng không phải là ký tự từ và để giữ chúng, chúng tôi có thể sử dụng từ điển để duy trì số lượng các dấu phân cách tương tự (trong trường hợp này là collections.Counter() sẽ thực hiện công việc).

Demo:

>>> s = "aaa#bbb*ccc#ddd*rkfh^ndjfh*dfehb*[email protected]*rjh" 
>>> delimiters = re.findall(r'\W', s) 
>>> first = delimiters[0] 
'#' 
>>> Counter(delimiters) 
Counter({'*': 5, '#': 2, '@': 1, '-': 1, '^': 1}) 
>>> 
>>> frequent = Counter(delimiters).most_common(1)[0][0] 
'*' 
>>> re.split(r'\{}|\{}'.format(first, frequent), s) 
['aaa', 'bbb', 'ccc', 'ddd', 'rkfh^ndjfh', 'dfehb', '[email protected]', 'rjh'] 

Lưu ý rằng nếu bạn đang đối phó với delimiters rằng có nhiều hơn một ký tự mà bạn có thể sử dụng re.escape() để thoát khỏi nhân vật regex đặc biệt (như *).

+1

Ah bạn đúng ++ – anubhava

+0

Câu hỏi thực sự được yêu cầu để tránh các bước sơ bộ để tìm các dấu phân cách đầu tiên và thường xuyên nhất. Nhưng nếu không thể tránh được những bước này, câu trả lời này sẽ giúp bạn thực hiện tốt. –

0

Tôi đã tìm thấy phương thức string.count() rất nhanh vì nó được triển khai trong C. Mọi thứ tránh vòng lặp thường sẽ nhanh hơn, ngay cả khi bạn lặp lại chuỗi nhiều lần.Đây có lẽ là giải pháp nhanh nhất:

>>> s = 'aaa*bbb#ccc*ddd' 
>>> a, b = s.count('*'), s.count('#') 
>>> if a == b: a, b = -s.find('*'), -s.find('#') 
... 
>>> s.split('*' if a > b else '#') 
['aaa', 'bbb#ccc', 'ddd'] 
+0

.count() và .find() nhanh hơn .split() và nếu .split() là cách nhanh nhất để chia nhỏ, điều này sẽ dễ dàng nhỏ hơn 5x tốc độ của giải pháp tối ưu. (tôi đoán ít hơn 2x) –

Các vấn đề liên quan