2013-06-07 38 views
5

Mặc dù tôi có đủ kiến ​​thức về regex trong mã giả, tôi gặp khó khăn trong việc dịch những gì tôi muốn làm trong php regex perl.
Tôi đang cố gắng sử dụng preg_match để trích xuất một phần biểu thức của mình.
Tôi có chuỗi sau ${classA.methodA.methodB(classB.methodC(classB.methodD)))} và tôi cần phải làm 2 việc:
PHP Regex preg_match extract

a. xác nhận các cú pháp

  • ${classA.methodA.methodB(classB.methodC(classB.methodD)))}hợp lệ
  • ${classA.methodA.methodB}hợp lệ
  • ${classA.methodA.methodB()}không hợp lệ
  • ${methodB(methodC(classB.methodD)))}không hợp lệ

b. Tôi cần phải trích lục các thông tin ${classA.methodA.methodB(classB.methodC(classB.methodD)))} nên trở

  1. classA
  2. methodA
  3. methodB (classB.methodC (classB.methodD)))

Tôi đã tạo mã này

$expression = '${myvalue.fdsfs.fsdf.blo(fsdf.fsfds(fsfs.fs))}'; 
$pattern = '/\$\{(?:([a-zA-Z0-9]+)\.)(?:([a-zA-Z\d]+)\.)*([a-zA-Z\d.()]+)\}/'; 
if(preg_match($pattern, $expression, $matches)) 
{ 
    echo 'found'.'<br/>'; 
    for($i = 0; $i < count($matches); $i++) 
     echo $i." ".$matches[$i].'<br/>'; 
} 

kết quả là:
tìm thấy
0 $ {myvalue.fdsfs.fsdf.blo (fsdf.fsfds (fsfs.fs))}
1 myValue
2 fsdf
3 Blo (fsdf.fsfds (fsfs.fs))

Rõ ràng Tôi đang gặp khó khăn để trích xuất các phương pháp lặp lại và nó không xác nhận nó đúng cách (trung thực tôi đã để lại nó lần cuối cùng khi tôi giải quyết vấn đề khác) vì vậy dấu ngoặc đơn trống được cho phép và không kiểm tra xem nó có mở ngoặc không phải được đóng lại.

Cảm ơn tất cả

CẬP NHẬT

X m.buettner

Nhờ sự giúp đỡ của bạn. Tôi đã thử một cách nhanh chóng để mã của bạn nhưng nó cho một vấn đề rất nhỏ, mặc dù tôi có thể bằng cách vượt qua nó. Vấn đề là như nhau của một trong những mã trước của tôi rằng tôi đã không gửi ở đây là khi tôi cố gắng chuỗi này:

$expression = '${myvalue.fdsfs}'; 

với nét hoa văn của bạn nó cho thấy:

found 
0 ${myvalue.fdsfs} 
1 myvalue.fdsfs 
2 myvalue 
3 
4 fdsfs 

Như bạn có thể xem dòng thứ ba được đánh bắt như một khoảng trắng không có mặt. Tôi không thể hiểu tại sao nó đã làm điều đó vì vậy bạn có thể đề nghị tôi làm thế nào để hoặc tôi phải sống với nó do hạn chế regex php?

Điều đó nói rằng tôi chỉ có thể cho bạn biết cảm ơn bạn. Không chỉ bạn trả lời cho vấn đề của tôi mà còn là bạn đã cố gắng nhập càng nhiều thông tin càng tốt với nhiều gợi ý về con đường thích hợp để làm theo khi phát triển các mẫu.

Một điều cuối cùng tôi (ngu ngốc) quên thêm một ít trường hợp quan trọng đó là nhiều tham số chia bằng dấu phẩy để

$expression = '${classA.methodAA(classB.methodBA(classC.methodCA),classC.methodCB)}'; 
$expression = '${classA.methodAA(classB.methodBA(classC.methodCA),classC.methodCB,classD.mehtodDA)}'; 

phải hợp lệ.

tôi chỉnh sửa để

$expressionPattern =    
     '/ 
     ^     # beginning of the string 
     [$][{]    # literal ${ 
     (     # group 1, used for recursion 
      (    # group 2 (class name) 
      [a-z\d]+  # one or more alphanumeric characters 
     )     # end of group 2 (class name) 
      [.]    # literal . 
      (    # group 3 (all intermediate method names) 
      (?:    # non-capturing group that matches a single method name 
       [a-z\d]+  # one or more alphanumeric characters 
       [.]   # literal . 
      )*    # end of method name, repeat 0 or more times 
     )     # end of group 3 (intermediate method names); 
      (    # group 4 (final method name and arguments) 
      [a-z\d]+  # one or or more alphanumeric characters 
      (?:    # non-capturing group for arguments 
       [(]   # literal (
       (?1)   # recursively apply the pattern inside group 1 
       (?:  # non-capturing group for multiple arguments   
        [,]  # literal , 
        (?1)  # recursively apply the pattern inside group 1 on parameters 
       )*   # end of multiple arguments group; repeat 0 or more times 
       [)]   # literal) 
      )?    # end of argument-group; make optional 
     )     # end of group 4 (method name and arguments) 
     )     # end of group 1 (recursion group) 
     [}]     # literal } 
     $     # end of the string 
     /ix'; 

X Casimir et Hippolyte

gợi ý của bạn này cũng tốt nhưng nó ngụ ý một chút tình hình phức tạp khi sử dụng mã này. Tôi có nghĩa là mã chính nó là dễ hiểu nhưng nó nhận được ít linh hoạt. Điều đó nói rằng nó cũng đã cho tôi rất nhiều thông tin chắc chắn có thể hữu ích trong tương lai.

X Denomales

Cám ơn sự ủng hộ của bạn, nhưng mã của bạn rơi khi tôi cố gắng này:

$sourcestring='${classA1.methodA0.methodA1.methodB1(classB.methodC(classB.methodD))}'; 

kết quả là:

Array 

( [0] => Mảng ( [0] => $ {classA1.methodA0.methodA1.methodB1 (classB.methodC (cl assB.methodD))} )

[1] => Array 
    (
     [0] => classA1 
    ) 

[2] => Array 
    (
     [0] => methodA0 
    ) 

[3] => Array 
    (
     [0] => methodA1.methodB1(classB.methodC(classB.methodD)) 
    ) 

) 

Nó phải là

[2] => Array 
    (
     [0] => methodA0.methodA1 
    ) 

[3] => Array 
    (
     [0] => methodB1(classB.methodC(classB.methodD)) 
    ) 

) 

hoặc

[2] => Array 
    (
     [0] => methodA0 
    ) 

[3] => Array 
    (
     [0] => methodA1 
    ) 

[4] => Array 
    (
     [0] => methodB1(classB.methodC(classB.methodD)) 
    ) 

) 

Trả lời

6

Đây là một điều khó khăn. Các mẫu đệ quy thường vượt ra ngoài những gì có thể với các biểu thức thông thường và thậm chí nếu có thể, nó có thể dẫn đến những biểu thức rất khó hiểu và duy trì.

Bạn đang sử dụng PHP và do đó PCRE, thực sự hỗ trợ cấu trúc regex đệ quy (?n). Như mô hình đệ quy của bạn là khá thường xuyên nó có thể tìm thấy một giải pháp phần nào thực tế bằng cách sử dụng regex.

Một báo trước tôi nên đề cập ngay lập tức: vì bạn cho phép và tùy ý số cuộc gọi phương thức "trung gian" cho mỗi cấp độ (trong đoạn mã fdsfsfsdf), bạn không thể nhận tất cả những điều này trong các lần chụp riêng biệt. Điều đó đơn giản là không thể với PCRE. Mỗi trận đấu sẽ luôn mang lại số lần chụp hữu hạn giống nhau, được xác định bởi số lượng mở ngoặc đơn mà mẫu của bạn chứa. Nếu nhóm chụp được sử dụng nhiều lần (ví dụ: sử dụng một cái gì đó như ([a-z]+\.)+) thì mỗi khi nhóm được sử dụng lần chụp trước đó sẽ bị ghi đè và bạn chỉ nhận được phiên bản cuối cùng. Do đó, tôi khuyên bạn nên chụp tất cả các cuộc gọi phương thức "trung gian" với nhau, và sau đó chỉ cần kết quả là explode.

Tương tự như vậy, bạn không thể (nếu bạn muốn) có được các ảnh chụp nhiều cấp độ lồng nhau cùng một lúc. Do đó, ảnh chụp mong muốn của bạn (vị trí cuối cùng bao gồm tất cả các mức lồng nhau) là tùy chọn duy nhất - sau đó bạn có thể áp dụng lại mẫu đó cho trận đấu cuối cùng đó để hạ cấp xuống.

Bây giờ cho các biểu hiện thực tế:

$pattern = '/ 
    ^     # beginning of the string 
    [$][{]    # literal ${ 
    (     # group 1, used for recursion 
     (     # group 2 (class name) 
     [a-z\d]+   # one or more alphanumeric characters 
    )     # end of group 2 (class name) 
     [.]     # literal . 
     (     # group 3 (all intermediate method names) 
     (?:    # non-capturing group that matches a single method name 
      [a-z\d]+  # one or more alphanumeric characters 
      [.]    # literal . 
     )*    # end of method name, repeat 0 or more times 
    )     # end of group 3 (intermediate method names); 
     (     # group 4 (final method name and arguments) 
     [a-z\d]+   # one or or more alphanumeric characters 
     (?:    # non-capturing group for arguments 
      [(]    # literal (
      (?1)   # recursively apply the pattern inside group 1 
      [)]    # literal) 
     )?    # end of argument-group; make optional 
    )     # end of group 4 (method name and arguments) 
    )      # end of group 1 (recursion group) 
    [}]     # literal } 
    $      # end of the string 
    /ix'; 

Một vài lưu ý chung: cho các biểu thức phức tạp (và trong hương vị regex có hỗ trợ nó), luôn sử dụng tự do khoảng cách x sửa đổi cho phép bạn giới thiệu khoảng trắng và nhận xét để định dạng biểu thức theo mong muốn của bạn.Nếu không có chúng, mẫu trông giống như sau:

'/^[$][{](([a-z\d]+)[.]((?:[a-z\d]+[.])*)([a-z\d]+(?:[(](?1)[)])?))[}]$/ix' 

Ngay cả khi bạn đã tự viết chính mình và bạn là người duy nhất làm việc trong dự án - hãy thử hiểu điều này một tháng kể từ bây giờ.

Thứ hai, tôi đã đơn giản hóa mẫu bằng cách sử dụng công cụ sửa đổi i phân biệt chữ hoa chữ thường. Nó chỉ đơn giản là loại bỏ một số lộn xộn, bởi vì bạn có thể bỏ qua các biến thể chữ hoa của chữ cái của bạn.

Thứ ba, lưu ý rằng tôi sử dụng các lớp ký tự đơn lẻ như [$][.] để thoát khỏi các ký tự nơi điều này có thể. Đó chỉ đơn giản là một vấn đề của hương vị, và bạn được tự do sử dụng các biến thể dấu gạch chéo ngược. Cá nhân tôi chỉ thích khả năng đọc của các lớp nhân vật (và tôi biết những người khác ở đây không đồng ý), vì vậy tôi cũng muốn giới thiệu cho bạn tùy chọn này.

Thứ tư, tôi đã thêm các neo quanh mẫu của bạn, để không thể có cú pháp không hợp lệ bên ngoài ${...}.

Cuối cùng, việc đệ quy hoạt động như thế nào? (?n) tương tự như một backreference \n, trong đó nó dùng để chỉ nhóm n (được tính bằng cách mở dấu ngoặc đơn từ trái sang phải). Sự khác biệt là một backreference cố gắng để phù hợp với một lần nữa những gì đã được kết hợp bởi nhóm n, trong khi (?n) áp dụng lại mẫu. Đó là (.)\1 khớp với bất kỳ ký tự nào hai lần liên tiếp, trong khi (.)(?1) khớp với bất kỳ ký tự nào và sau đó áp dụng lại mẫu, do đó khớp với một ký tự tùy ý khác. Nếu bạn sử dụng một trong những cấu trúc (?n) này trong nhóm thứ n, bạn sẽ nhận được đệ quy. (?0) hoặc (?R) đề cập đến toàn bộ mẫu. Đó là tất cả sự kỳ diệu.

Các mô hình trên áp dụng cho các đầu vào

'${abc.def.ghi.jkl(mno.pqr(stu.vwx))}' 

sẽ dẫn đến việc chụp

0 ${abc.def.ghi.jkl(mno.pqr(stu.vwx))} 
1 abc.def.ghi.jkl(mno.pqr(stu.vwx)) 
2 abc 
3 def.ghi. 
4 jkl(mno.pqr(stu.vwx)) 

Lưu ý rằng có một vài sự khác biệt với kết quả đầu ra bạn thực sự mong đợi:

0 là toàn bộ kết hợp (và trong trường hợp này chỉ là chuỗi đầu vào một lần nữa). PHP sẽ luôn báo cáo điều này trước tiên, vì vậy bạn không thể loại bỏ nó.

1 là nhóm chụp đầu tiên bao quanh phần đệ quy. Bạn không cần điều này trong đầu ra, nhưng không may là (?n) không thể tham chiếu đến các nhóm không chụp, vì vậy bạn cũng cần điều này.

2 là tên lớp như mong muốn.

3 là danh sách tên phương pháp trung gian, cộng với dấu chấm. Sử dụng explode thật dễ dàng để trích xuất tất cả các tên phương thức từ điều này.

4 là tên phương thức cuối cùng, với danh sách đối số tùy chọn (đệ quy). Bây giờ bạn có thể thực hiện việc này và áp dụng lại mẫu nếu cần. Lưu ý rằng đối với một cách tiếp cận hoàn toàn đệ quy, bạn có thể muốn sửa đổi mô hình một chút.Đó là: xóa ${} trong bước đầu tiên riêng biệt, sao cho toàn bộ mẫu có cùng mẫu (đệ quy) giống như lần chụp cuối cùng và bạn có thể sử dụng (?0) thay vì (?1). Sau đó, khớp, xóa tên phương thức và dấu ngoặc đơn và lặp lại, cho đến khi bạn không còn dấu ngoặc đơn trong lần chụp cuối cùng.

Để biết thêm thông tin về đệ quy, hãy xem PHP's PCRE documentation.


Để minh họa điểm cuối cùng của tôi, đây là một đoạn mã sẽ trích xuất toàn bộ các yếu tố một cách đệ quy:

if(!preg_match('/^[$][{](.*)[}]$/', $expression, $matches)) 
    echo 'Invalid syntax.'; 
else 
    traverseExpression($matches[1]); 

function traverseExpression($expression, $level = 0) { 
    $pattern = '/^(([a-z\d]+)[.]((?:[a-z\d]+[.])*)([a-z\d]+(?:[(](?1)[)])?))$/i'; 
    if(preg_match($pattern, $expression, $matches)) { 
     $indent = str_repeat(" ", 4*$level); 
     echo $indent, "Class name: ", $matches[2], "<br />"; 
     foreach(explode(".", $matches[3], -1) as $method) 
      echo $indent, "Method name: ", $method, "<br />"; 
     $parts = preg_split('/[()]/', $matches[4]); 
     echo $indent, "Method name: ", $parts[0], "<br />"; 
     if(count($parts) > 1) { 
      echo $indent, "With arguments:<br />"; 
      traverseExpression($parts[1], $level+1); 
     } 
    } 
    else 
    { 
     echo 'Invalid syntax.'; 
    } 
} 

Lưu ý một lần nữa, rằng tôi không khuyên bạn sử dụng các mô hình như một lớp lót, nhưng câu trả lời này đã đủ dài rồi.

+0

Khi tôi thử $ expression = '$ {myvalue.fdsfs}'; – user2463968

+0

@ user2463968 Vậy thì sao? Bạn nhận được một tên lớp, không có phương thức trung gian và phương thức cuối cùng mà không có đối số. Đó không phải là ý định của bạn? –

4

bạn có thể làm xác nhận và khai thác với cùng một khuôn mẫu, ví dụ:

$subjects = array(
'${classA.methodA.methodB(classB.methodC(classB.methodD))}', 
'${classA.methodA.methodB}', 
'${classA.methodA.methodB()}', 
'${methodB(methodC(classB.methodD))}', 
'${classA.methodA.methodB(classB.methodC(classB.methodD(classC.methodE)))}', 
'${classA.methodA.methodB(classB.methodC(classB.methodD(classC.methodE())))}' 
); 

$pattern = <<<'LOD' 
~ 
# definitions 
(?(DEFINE)(?<vn>[a-z]\w*+)) 

# pattern 
^\$\{ 
    (?<classA>\g<vn>)\. 
    (?<methodA>\g<vn>)\. 
    (?<methodB> 
     \g<vn> ( 
      \(\g<vn> \. \g<vn> (?-1)?+ \) 
     )?+ 
    ) 
}$ 

~x 
LOD; 

foreach($subjects as $subject) { 
    echo "\n\nsubject: $subject"; 
    if (preg_match($pattern, $subject, $m)) 
     printf("\nclassA: %s\nmethodA: %s\nmethodB: %s", 
      $m['classA'], $m['methodA'], $m['methodB']); 
    else 
     echo "\ninvalid string";  
} 

Regex exp làn đường:
¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯

Ở cuối mẫu, bạn có thể thấy công cụ sửa đổi x cho phép dấu cách, dòng mới và chú thích bên trong mẫu.

Đầu tiên mẫu bắt đầu bằng định nghĩa của một nhóm có tên vn (tên biến), tại đây bạn có thể xác định cách classA hoặc methodB trông như thế nào cho tất cả mẫu. Sau đó, bạn có thể tham khảo định nghĩa này trong tất cả các mẫu với \g<vn>

Lưu ý rằng bạn có thể xác định xem bạn có muốn loại tên khác nhau cho các lớp và phương pháp thêm định nghĩa khác hay không.Ví dụ:

(?(DEFINE)(?<cn>....)) # for class name 
(?(DEFINE)(?<mn>....)) # for method name 

Các mô hình riêng của mình:

(?<classA>\g<vn>) chụp trong nhóm có tên classA với mô hình quy định tại vn

điều tương tự cho methodA

methodB là khác nhau vì nó có thể chứa lồng nhau parenthe sis, đó là lý do tại sao tôi sử dụng một mô hình đệ quy cho phần này.

Chi tiết:

\g<vn>   # the method name (methodB) 
(    # open a capture group 
    \(  # literal opening parenthesis 
    \g<vn> \. \g<vn> # for classB.methodC⑴ 
    (?-1)?+ # refer the last capture group (the actual capture group) 
       # one or zero time (possessive) to allow the recursion stop 
       # when there is no more level of parenthesis 
    \)   # literal closing parenthesis 
)?+   # close the capture group 
       # one or zero time (possessive) 
       # to allow method without parameters 

bạn có thể thay thế nó bằng \g<vn>(?>\.\g<vn>)+ nếu bạn muốn cho phép nhiều hơn một phương pháp.

Về quantifiers sở hữu:

Bạn có thể thêm + sau một lượng hóa (*+?) để làm cho nó sở hữu, lợi thế là các động cơ regex biết rằng nó không cần phải quay lại để kiểm tra cách khác để phù hợp với một mẫu con. Regex sau đó hiệu quả hơn.

+0

đánh bại tôi để nó bằng 27 giây: D 1 cho các nhóm chụp được đặt tên. Tôi nên thực hiện thói quen đó cũng như –

+0

@ m.buettner: Tôi nghĩ chúng tôi đã tạo ra một chủ đề lịch sử! –

+0

Đề xuất của bạn cũng tốt nhưng nó hàm ý một tình huống phức tạp nhỏ khi sử dụng mã này. Tôi có nghĩa là mã chính nó là dễ hiểu nhưng nó nhận được ít linh hoạt. Điều đó nói rằng nó cũng đã cho tôi rất nhiều thông tin chắc chắn có thể hữu ích trong tương lai. – user2463968

2

Mô tả

biểu hiện này sẽ phù hợp và chụp chỉ ${classA.methodA.methodB(classB.methodC(classB.methodD)))} hoặc ${classA.methodA.methodB} định dạng.

(?:^|\n|\r)[$][{]([^.(}]*)[.]([^.(}]*)[.]([^(}]*(?:[(][^}]+[)])?)[}](?=\n|\r|$)

enter image description here

Groups

Nhóm 0 được toàn bộ trận đấu từ các dấu hiệu bắt đầu đôla để khung gần nguệch ngoạc

  1. được Class
  2. được là người đầu tiên phương pháp
  3. là phương pháp thứ hai được theo sau bởi tất cả văn bản tối đa nhưng không bao gồm dấu ngoặc nhọn gần. Nếu nhóm này có dấu ngoặc tròn mở mà là trống () sau đó trận đấu này sẽ thất bại

PHP Mã số Ví dụ:

<?php 
$sourcestring="${classA1.methodA1.methodB1(classB.methodC(classB.methodD)))} 
${classA2.methodA2.methodB2} 
${classA3.methodA3.methodB3()} 
${methodB4(methodC4(classB4.methodD)))} 
${classA5.methodA5.methodB5(classB.methodC(classB.methodD)))}"; 
preg_match_all('/(?:^|\n|\r)[$][{]([^.(}]*)[.]([^.(}]*)[.]([^(}]*(?:[(][^}]+[)])?)[}](?=\n|\r|$)/im',$sourcestring,$matches); 
echo "<pre>".print_r($matches,true); 
?> 

$matches Array: 
(
    [0] => Array 
     (
      [0] => ${classA1.methodA1.methodB1(classB.methodC(classB.methodD)))} 
      [1] => 
${classA2.methodA2.methodB2} 
      [2] => 
${classA5.methodA5.methodB5(classB.methodC(classB.methodD)))} 
     ) 

    [1] => Array 
     (
      [0] => classA1 
      [1] => classA2 
      [2] => classA5 
     ) 

    [2] => Array 
     (
      [0] => methodA1 
      [1] => methodA2 
      [2] => methodA5 
     ) 

    [3] => Array 
     (
      [0] => methodB1(classB.methodC(classB.methodD))) 
      [1] => methodB2 
      [2] => methodB5(classB.methodC(classB.methodD))) 
     ) 

) 

Phủ nhận

  • Tôi đã thêm một số vào cuối của lớp và tên phương pháp để giúp illistrate những gì đang xảy ra trong các nhóm
  • Văn bản mẫu được cung cấp trong OP không có dấu ngoặc tròn mở và đóng cân bằng.
  • Mặc dù () sẽ không được phép (()) sẽ được phép