2009-01-20 22 views
8

Tôi đã viết một hệ thống quản lý nội dung sử dụng biểu thức chính quy phía máy chủ để thoát khỏi ký hiệu và trả lời trang ngay trước khi nó được gửi tới trình duyệt của khách hàng. Cụm từ thông dụng là chú ý các ký hiệu và đã được thoát hoặc là một phần của thực thể HTML. Ví dụ, sau đây:Cụm từ thông dụng để loại bỏ HTML và khi tuân thủ CDATA

a & b, c & d, © 2009

được thay đổi như sau:

a & b, c & d, © 2009

(Chỉ & đầu tiên được sửa đổi.) Đây là biểu hiện thường xuyên, được thực hiện và sửa đổi từ một helper Rails:

html.gsub(/&(?!([a-zA-Z][a-zA-Z0-9]*|(#\d+));)/) { |special| ERB::Util::HTML_ESCAPE[special] } 

Mặc dù công trình này rất tuyệt nhưng nó vẫn gặp sự cố. Cụm từ thông dụng không nhận biết được bất kỳ số <![CDATA[ hoặc ]]> nào có thể bao quanh các ký hiệu không thoát. Điều này là cần thiết cho JavaScript nhúng để không bị ảnh hưởng. Ví dụ, điều này:

<script type="text/javascript"> 
    // <![CDATA[ 
    if (a && b) doSomething(); 
    // ]]> 
</script> 

là không may rendered như thế này:

<script type="text/javascript"> 
    // <![CDATA[ 
    if (a &amp;&amp; b) doSomething(); 
    // ]]> 
</script> 

trong đó tất nhiên các công cụ JavaScript không hiểu.

Câu hỏi của tôi là: Có cách nào để sửa đổi cụm từ thông dụng để thực hiện chính xác như bây giờ với ngoại lệ là nó để lại văn bản bên trong phần CDATA bị ảnh hưởng không?

Vì cụm từ thông dụng không đơn giản để bắt đầu, câu hỏi này có thể dễ trả lời hơn: Có thể viết cụm từ thông dụng sẽ thay đổi tất cả các chữ cái trong một khoảng thời gian ngoại trừ các chữ cái giữa '<' và '>'? Ví dụ: một cái sẽ thay đổi "some <words> are < safe! >" thành ".... <words> ... < safe! >"?

+0

Tôi sẽ ngạc nhiên nếu điều này có thể được giải quyết bằng cách sử dụng chỉ regexes, vì vậy tôi là tất cả các háo hức hơn để xem ai đó trả lời câu hỏi này :-) –

+0

Làm thế nào người dùng sẽ hiển thị chuỗi thực tế '&' nếu họ muốn ? (ví dụ: trong một mẫu HTML) – orip

Trả lời

7

Bạn đã yêu cầu! : D

/&(?!(?:[a-zA-Z][a-zA-Z0-9]*|#\d+);) 
(?!(?>(?:(?!<!\[CDATA\[|\]\]>).)*)\]\]>)/xm 

Dòng đầu tiên là regex gốc của bạn.Các lookahead khớp với nhau nếu có một chuỗi kết thúc CDATA (]]>) ở phía trước, trừ khi có một chuỗi mở (<!CDATA[) giữa đây và ở đó. Giả sử tài liệu được hình thành tối thiểu, điều đó có nghĩa là vị trí hiện tại nằm trong phần CDATA.

Rất tiếc, tôi đã bị lạc hậu: bằng cách sử dụng lookahead tích cực, tôi đã khớp các ký hiệu "trần truồng" chỉ trong các phần CDATA. Tôi đã thay đổi nó thành một cái nhìn tiêu cực, vì vậy bây giờ nó hoạt động đúng.

Bằng cách này, regex này hoạt động trong RegexBuddy ở chế độ Ruby, nhưng không phải tại the rubular site. Tôi nghi ngờ Rubular sử dụng một phiên bản cũ của Ruby với hỗ trợ regex ít mạnh mẽ hơn; bất cứ ai có thể xác nhận điều đó? (Như bạn có thể đoán, tôi không phải là lập trình viên Ruby.)

EDIT: Vấn đề tại Rubular là tôi đã sử dụng 's' làm công cụ sửa đổi (có nghĩa là dấu chấm-tất cả mọi thứ), nhưng Ruby sử dụng ' m 'cho điều đó.

+0

Giải pháp tốt. Điều này đã cho tôi một thời gian để grok. Dưới đây là giải thích chi tiết nếu có ai khác quan tâm: http://bitkickers.blogspot.com/2009/01/regular-expression-negative-lookahead_31.html –

+1

"Tôi nghĩ điều này là tự giải thích. Hẹn gặp lại lần sau!" : D –

0

tôi đã thực hiện một cái gì đó tương tự như ở đây:
Best way to encode text data for XML

May mắn thay, trong trường hợp của tôi CDATA không phải là một vấn đề.

một vấn đề là gì là bạn phải cẩn thận rằng biểu thức không phải là tham lam hoặc bạn sẽ kết thúc với một cái gì đó như thế này:

.... <words> are < safe! >

0

tôi nghiêm túc nghi ngờ rằng những gì bạn đang cố gắng để thực hiện là một cái gì đó bạn có thể làm bằng cách sử dụng một biểu thức chính quy một mình. Regexps nổi tiếng là xấu khi bàn giao một cách chính xác.

Có thể bạn nên sử dụng trình phân tích cú pháp XML và không thoát khỏi nội dung CDATA.

3

Không sử dụng cụm từ thông dụng cho việc này. Đó là một ý tưởng kinh khủng, khủng khiếp. Thay vào đó, chỉ cần mã hóa HTML bất kỳ thứ gì mà bạn xuất ra có thể có một ký tự trong đó. Như thế này:

require 'cgi' 
print CGI.escape("All of this is HTML encoded!") 
+0

Điều này sẽ không làm cho các thực thể đã thoát đã được mã hóa hai lần không? (ví dụ: '& '->' & amp; '?) –

+1

Tôi không muốn thoát khỏi mọi thứ vì một vài lý do, một là (như Ben Blank đã nói) & sẽ trở thành & amp; nhưng cũng bởi vì tôi không muốn các ký tự trong JavaScript nội tuyến được thoát, do đó cần loại trừ các phần CDATA. – Nick

+0

Rất tiếc. Tôi nên nói unescape thay thế. –

1

Đã hoạt động! Tại Rubular Tôi đã phải thay đổi các tùy chọn từ /xs thành /m (và tôi đã xóa khoảng trắng tách riêng hai phần của regex như bạn đã trình bày ở trên).

Bạn có thể thấy cụm từ thông dụng này hoạt động cùng với chuỗi mẫu tại http://www.rubular.com/regexes/5855.

Trong trường hợp đó Rubular permalink là không thực sự vĩnh viễn, đây là những gì tôi nhập cho các biểu thức chính quy:

/&(?!(?:[a-zA-Z][a-zA-Z0-9]*|#\d+);)(?!(?>(?:(?!<!\[CDATA\[|\]\]>).)*)\]\]>)/m 

Và đây là chuỗi thử nghiệm:

<p>a & b</p> 
<p>c &amp; d</p> 
<script type="text/javascript"> 
    // <![CDATA[ 
    if (a && b) doSomething('a & b &amp; c'); 
    // ]]> 
</script> 
<p>a & b</p> 
<p>c &amp; d</p> 

Chỉ có hai trận đấu ampersands - số a & b ở trên cùng và a & b ở dưới cùng. Các ký tự đã được thoát ra dưới dạng &amp; và tất cả các ký hiệu và dấu (thoát hoặc không) giữa các số <![CDATA[]]> đều bị bỏ lại một mình.

Vì vậy, mã cuối cùng của tôi bây giờ đây là:

html.gsub(/&(?!(?:[a-zA-Z][a-zA-Z0-9]*|#\d+);)(?!(?>(?:(?!<!\[CDATA\[|\]\]>).)*)\]\]>)/m, '&amp;') 

Thank you very much Alan. Đây chính xác là những gì tôi cần.

+0

Ach! Tôi tiếp tục quên đi Ruby bằng cách sử dụng công cụ sửa đổi 'm' có nghĩa là những gì mọi người khác sử dụng 's' cho. Tôi sẽ sửa nó. –

+0

Trong PHP, bạn cần sử dụng tùy chọn/s (PCRE_DOTALL). PCRE có dấu ngắt dòng hoặc khoảng trống không hoạt động đối với tôi, ngay cả khi sử dụng các tùy chọn/m (PCRE_MULTILINE) và/hoặc/x (PCRE_EXTENDED). – feeela

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