2015-02-13 16 views
11

Tôi có dữ liệu sau bên dưới, trong đó {n} đại diện cho trình giữ chỗ.Thay thế mỗi thể hiện giữa hai ký tự

{n}{n}A{n}{n}A{n} 
{n}A{n}{n}{n}{n}A 
{n}{n}A{n}A{n}{n} 
{n}{n}{n}A{n}A{n}B 
{n}A{n}{n}B{n}{n} 
A{n}B{n}{n}{n}{n} 

Tôi muốn thay thế từng phiên bản của trình giữ chỗ giữa hai ký tự A với ví dụ: C. Tôi đã viết regex sau đây cho điều này và tôi đang sử dụng chức năng preg_replace.

$str = preg_replace('~(?<=A)(\{n\})*(?=A)~', 'C', $str); 

Vấn đề là nó thay thế tất cả các trường hợp giữa hai chữ A với một C. Làm thế nào tôi có thể sửa chữa regex của tôi hoặc cuộc gọi preg_replace để thay thế từng cá thể của trình giữ chỗ bằng C?

Đây phải là đầu ra của tôi.

{n}{n}ACCA{n} 
{n}ACCCCA 
{n}{n}ACA{n}{n} 
{n}{n}{n}ACA{n}B 
{n}A{n}{n}B{n}{n} 
A{n}B{n}{n}{n}{n} 

Nhưng hiện tại nó xuất kết quả này.

{n}{n}ACA{n} 
{n}ACA 
{n}{n}ACA{n}{n} 
{n}{n}{n}ACA{n}B 
{n}A{n}{n}B{n}{n} 
A{n}B{n}{n}{n}{n} 
+0

Điều gì xảy ra trong trường hợp này: 'A {n} A {n} A'? 'ACA {n} A' hoặc' ACACA'? –

Trả lời

8

Bạn có thể giải quyết sự cố bằng cách neo với \G.

$str = preg_replace('~(?:\G(?!\A)|({n})*A(?=(?1)++A))\K{n}~', 'C', $str); 

Tính năng \G là một neo có thể khớp với một trong hai vị trí; sự bắt đầu của vị trí chuỗi hoặc vị trí ở cuối của trận đấu cuối cùng. Trình tự thoát \K đặt lại điểm bắt đầu của kết quả được báo cáo và mọi ký tự đã tiêu thụ trước đó sẽ không còn được bao gồm.

Để giảm số lượng tùy ý, bạn có thể sử dụng một biểu thức phức tạp hơn:

$str = preg_replace('~\G(?!\A)(?:{n} 
         |A(?:[^A]*A)+?((?=(?:{n})++A)\K{n} 
         |(*COMMIT)(*F))) 
         |[^A]*A(?:[^A]*A)*?(?1)~x', 'C', $str); 
+3

[+] (http://en.wikipedia.org/wiki/Plus_and_minus_signs) 1 để sử dụng tuyệt vời '\ K'. – Unihedron

4
(?<=A){n}(?=(?:{n})*A)|\G(?!^){n} 

Bạn có thể thử này. Thay thế bằng C. Ở đây bạn phải sử dụng \G để khẳng định vị trí ở cuối trận đấu trước đó hoặc bắt đầu chuỗi cho trận đấu đầu tiên.

Để bạn có thể đối sánh sau trận đấu đầu tiên của mình. Xem bản trình diễn.

https://regex101.com/r/wU4xK1/7

đây đầu tiên bạn kết hợp {n} trong đó có A đằng sau nó và A sau khi nó có thể có {n} ở giữa. Sau khi chụp, bạn sử dụng \G để đặt lại về kết thúc trận đấu trước đó và sau đó tiếp tục thay thế {n} được tìm thấy.

$re = "/(?<=A){n}(?=(?:{n})*A)|\\G(?!^){n}/"; 
$str = "{n}{n}A{n}{n}A{n}\n{n}A{n}{n}{n}{n}A\n{n}{n}A{n}A{n}{n}\n{n}{n}{n}A{n}A{n}B\n{n}A{n}{n}B{n}{n}\nA{n}B{n}{n}{n}{n}"; 
$subst = "C"; 

$result = preg_replace($re, $subst, $str); 
7

Giải pháp tiết kiệm hơn nhưng dễ hiểu hơn là sử dụng biểu thức ban đầu để chia văn bản thành các nhóm; sau đó áp dụng việc chuyển đổi cá nhân bên trong mỗi nhóm:

$text = preg_replace_callback('~(?<=A)(?:\{n\})*(?=A)~', function($match) { 
    // simple replacement inside 
    return str_replace('{n}', 'C', $match[0]); 
}, $text); 

Tôi đã thực hiện một tinh chỉnh nhỏ để biểu thức để thoát khỏi việc bắt giữ trí nhớ, đó là không cần thiết, bằng cách sử dụng (?:...).

+2

Thành thật mà nói, có lẽ là giải pháp nhanh hơn nếu hiệu suất là một vấn đề. (+1) – hwnd

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