2008-11-06 32 views
9

Ok, vì vậy tôi có regex này:Tôi có thể tối ưu hóa regex điện thoại này không?

(|^|>)(((((((\+|00)(31|32)()?(\(0\))?)|0)([0-9]{2})(-)?()?)?)([0-9]{7}))|((((((\+|00)(31|32)()?(\(0\))?)|0)([0-9]{3})(-)?()?)?)([0-9]{6}))|((((((\+|00)(31|32)()?(\(0\))?)|0)([0-9]{1})(-)?()?)?)([0-9]{8})))(|$|<) 

Nó định dạng số điện thoại người Hà Lan và Bỉ (Tôi chỉ muốn những vì 31 và 32 là mã quốc gia).

Không có nhiều thú vị để giải mã nhưng bạn có thể thấy nó cũng bị trùng lặp nhiều. nhưng bây giờ nó không xử lý nó rất chính xác

Tất cả các số điện thoại được định dạng sau châu Âu được chấp nhận

00312
0031223234567 
0031612345678 
+31(0)20-1234567 
+31(0)223-234567 
+31(0)6-12345678 
020-1234567 
0223-234567 
06-12345678 
02
0223234567 
0612345678 

và những định dạng sai sau đây không

06-1234567 (mobile phone number in the Netherlands should have 8 numbers after 06) 
0223-1234567 (area code with home phone) 

như trái ngược với này đó là tốt .

020-1234567 (area code with 3 numbers has 7 numbers for the phone as opposed to a 4 number area code which can only have 6 numbers for phone number) 

Như bạn thấy đó là '-' nhân vật mà làm cho nó một chút khó khăn nhưng tôi cần nó ở đó vì đó là một phần của định dạng thường được sử dụng bởi những người, và tôi muốn để có thể phân tích chúng tất cả các.

Bây giờ là câu hỏi của tôi ... bạn có thấy một cách để đơn giản hóa regex này (hoặc thậm chí cải thiện nó nếu bạn thấy một lỗi trong đó), trong khi vẫn giữ nguyên quy tắc?

Bạn có thể kiểm tra nó ở regextester.com

(The '(|^|>)' là để kiểm tra xem nó là lúc bắt đầu của một từ có khả năng nó được đi trước bởi hoặc là một dòng mới hoặc một ' > '. Tôi tìm kiếm các số điện thoại trong các trang HTML.)

+0

Câu hỏi đầu tiên của tôi là: bạn có thực sự cần TẤT CẢ những ảnh chụp đó không? Bạn không thể chỉ lấy những phần quan trọng và định dạng lại. Các bộ phận thích hợp là gì? – Axeman

+0

không có tôi tìm kiếm các phonenumbers trong một loạt các văn bản tôi không biết nơi số lượng và theo cách thông thường của nó định dạng. sau khi tôi tìm thấy nó tôi cơ bản không cần nó nữa – youri

Trả lời

12

Quan sát đầu tiên: đọc regex là một cơn ác mộng. Nó kêu lên cho chế độ Perl/x.

Quan sát thứ hai: có rất nhiều và rất nhiều dấu ngoặc đơn trong biểu thức (42 nếu tôi đếm chính xác; và 42 là, tất nhiên, "Câu trả lời cho cuộc sống, vũ trụ và mọi thứ" - xem Douglas Adams "Hướng dẫn của Hitchiker về Thiên hà" nếu bạn cần giải thích đó).

Ghi chú Lizard mà bạn sử dụng '(-)?()?' nhiều lần. Không có lợi thế rõ ràng so với '-? ?' hoặc có thể '[- ]?', trừ khi bạn thực sự có ý định ghi dấu chấm câu một cách riêng biệt (nhưng có quá nhiều dấu ngoặc đơn làm việc '$ n' cứng).

Vì vậy, chúng ta hãy cố gắng chỉnh sửa một bản sao của một lót của bạn:

(|^|>) 
(
    ((((((\+|00)(31|32)()?(\(0\))?)|0)([0-9]{2})(-)?()?)?)([0-9]{7})) | 
    ((((((\+|00)(31|32)()?(\(0\))?)|0)([0-9]{3})(-)?()?)?)([0-9]{6})) | 
    ((((((\+|00)(31|32)()?(\(0\))?)|0)([0-9]{1})(-)?()?)?)([0-9]{8})) 
) 
(|$|<) 

OK - bây giờ chúng ta có thể thấy cấu trúc bình thường của biểu thức chính quy của bạn.

Có thể phân tích nhiều hơn nữa từ đây. Có, có thể có những cải tiến lớn đối với cụm từ thông dụng. Đầu tiên, hiển nhiên, một là trích xuất phần tiền tố quốc tế và áp dụng một lần (tùy chọn hoặc yêu cầu số 0 đứng đầu) và sau đó áp dụng các quy tắc quốc gia.

(|^|>) 
(
    (((\+|00)(31|32)()?(\(0\))?)|0) 
    (((([0-9]{2})(-)?()?)?)([0-9]{7})) | 
    (((([0-9]{3})(-)?()?)?)([0-9]{6})) | 
    (((([0-9]{1})(-)?()?)?)([0-9]{8})) 
) 
(|$|<) 

Sau đó, chúng ta có thể đơn giản hóa việc chấm câu như đã nói trước đây, và loại bỏ một số ngoặc plausibly dư thừa, và nâng cao nhận dạng mã quốc gia:

(|^|>) 
(
    (((\+|00)3[12] ?(\(0\))?)|0) 
    (((([0-9]{2})-? ?)?)[0-9]{7}) | 
    (((([0-9]{3})-? ?)?)[0-9]{6}) | 
    (((([0-9]{1})-? ?)?)[0-9]{8}) 
) 
(|$|<) 

Chúng ta có thể quan sát rằng regex không thi hành các quy định về mã số điện thoại di động (vì vậy nó không nhấn mạnh rằng '06' được theo sau bởi 8 chữ số, ví dụ). Nó cũng dường như cho phép mã số trao đổi 1, 2 hoặc 3 chữ số trở thành tùy chọn, ngay cả với tiền tố quốc tế - có thể không phải là những gì bạn nghĩ và sửa lỗi sẽ xóa thêm một số dấu ngoặc đơn. Chúng tôi có thể xóa thêm các dấu ngoặc đơn sau đó, dẫn đến:

(|^|>) 
(
    (((\+|00)3[12] ?(\(0\))?)|0) # International prefix or leading zero 
    ([0-9]{2}-? ?[0-9]{7}) |  # xx-xxxxxxx 
    ([0-9]{3}-? ?[0-9]{6}) |  # xxx-xxxxxx 
    ([0-9]{1}-? ?[0-9]{8})   # x-xxxxxxxx 
) 
(|$|<) 

Và bạn có thể tìm hiểu thêm về tối ưu hóa từ đây, tôi hy vọng.

+1

cảm ơn bạn tôi đã phá vỡ nó cho bản thân của tôi để xem nếu tôi có thể đạt được điều này nhưng tôi phải làm điều gì đó sai ... cảm ơn điều này thực sự hữu ích – youri

+1

bump rất cũ nhưng tôi chỉ nhìn thấy một phần khoảng 42 ... thats tốt đẹp: P cổ vũ giao phối: P – youri

+0

làm thế nào bạn có thể làm việc này với PHP và preg_replace? – Sanne

8

Chúa tể Toàn năng, thật là một mớ hỗn độn! :) Nếu bạn có các quy tắc ngữ nghĩa hoặc kinh doanh cấp cao (ví dụ như các quy tắc mà bạn mô tả nói về số châu Âu, số ở Hà Lan, v.v.), bạn có thể được phục vụ tốt hơn để phá vỡ phép thử regexp đơn đó thành một số phép thử regexp riêng lẻ, một cho mỗi quy tắc cấp cao của bạn.

if number =~ /...../ # Dutch mobiles 
    # ... 
elsif number =~ /..../ # Belgian landlines 
    # ... 
# etc. 
end 

Sẽ dễ dàng hơn một chút để đọc và duy trì và thay đổi theo cách đó.

+0

Và đặt hàng các bài kiểm tra của bạn bằng nhiều khả năng để phù hợp (giả sử bạn biết nhân khẩu học cũng đủ). – tvanfosson

+0

@tvanfosson: Chắc chắn; đã đồng ý. – Pistos

+0

mà tôi đã không nghĩ rằng: P cảm ơn :) – youri

3

Chia thành nhiều biểu thức. Ví dụ (pseudo-code) ...

phone_no_patterns = [ 
    /[0-9]{13}/, # 00312
    /+(31|32)\(0\)\d{2}-\d{7}/ # +31(0)20-1234567 
    # ..etc.. 
] 
def check_number(num): 
    for pattern in phone_no_patterns: 
     if num matches pattern: 
      return match.groups 

Sau đó, bạn chỉ cần lặp trên mỗi mẫu, kiểm tra nếu mỗi người phù hợp ..

tách các mô hình lên làm cho nó dễ dàng để sửa chữa con số cụ thể đang gây ra vấn đề (mà sẽ là khủng khiếp với regex khối duy nhất)

2

Nó không phải là một tối ưu hóa, nhưng bạn sử dụng

(-)?()? 

ba lần trong regex của bạn.Điều này sẽ khiến bạn để phù hợp trên các số điện thoại như thế này

+31(0)6-12345678 
+31(0)6 12345678 

nhưng cũng sẽ phù hợp với số có chứa một dấu gạch ngang theo sau là một không gian, giống như

+31(0)6- 12345678 

Bạn có thể thay

(-)?()? 

với

(-|)? 

để khớp với dấu gạch ngang hoặc một khoảng trắng.

+0

tốt hơn nhưng '[-]? ' –

+0

Điều đó là tốt hơn. Giải pháp của bạn tiết kiệm một nhân vật. Tôi đã tự cứu mình. :) –

+0

tôi đã không thông báo tôi đã làm điều đó nhờ – youri

3

(31 | 32) có vẻ không tốt. Khi kết hợp 32, công cụ regex đầu tiên sẽ thử 31 (2 ký tự), thất bại và quay lại hai ký tự để phù hợp với 31. Hiệu quả hơn để khớp với 3 (một ký tự), thử 1 (thất bại), quay lại một ký tự và phù hợp 2.

Tất nhiên, regex của bạn không thành công trên 0.800 số; chúng không phải 10 chữ số.

+0

tôi không muốn 0.800 số nhưng phần khác của bình luận của bạn là hữu ích nhờ. – youri

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