Làm cách nào để thay thế URL đơn giản bằng các liên kết?


453

Tôi đang sử dụng chức năng bên dưới để khớp các URL bên trong một văn bản nhất định và thay thế chúng cho các liên kết HTML. Biểu thức chính quy đang hoạt động rất tốt, nhưng hiện tại tôi chỉ thay thế trận đấu đầu tiên.

Làm cách nào tôi có thể thay thế tất cả URL? Tôi đoán tôi nên sử dụng lệnh exec , nhưng tôi không thực sự tìm ra cách để làm điều đó.

function replaceURLWithHTMLLinks(text) {
    var exp = /(\b(https?|ftp|file):\/\/[-A-Z0-9+&@#\/%?=~_|!:,.;]*[-A-Z0-9+&@#\/%=~_|])/i;
    return text.replace(exp,"<a href='$1'>$1</a>"); 
}

Câu trả lời:


350

Trước hết, cuộn regrec của riêng bạn để phân tích URL là một ý tưởng tồi tệ . Bạn phải tưởng tượng đây là một vấn đề đủ phổ biến mà ai đó đã viết, gỡ lỗi và kiểm tra một thư viện cho nó, theo RFC . Các URI rất phức tạp - hãy kiểm tra mã để phân tích cú pháp URL trong Node.js và trang Wikipedia trên các lược đồ URI .

Có rất nhiều trường hợp cạnh khi phân tích cú pháp URL: tên miền quốc tế , TLD thực tế ( .museum) so với .etcTLD không tồn tại ( ), dấu câu lạ bao gồm dấu ngoặc đơn , dấu chấm câu ở cuối URL, tên máy chủ IPV6, v.v.

Tôi đã nhìn vào một tấn của các thư viện , và có một vài giá trị sử dụng bất chấp một số nhược điểm:

Các thư viện mà tôi đã nhanh chóng bị loại khỏi nhiệm vụ này:

Nếu bạn nhấn mạnh vào một biểu thức chính quy, toàn diện nhất là regrec URL từ Thành phần , mặc dù nó sẽ phát hiện sai một số TLD hai chữ cái không tồn tại bằng cách xem xét nó.


3
Thật đáng tiếc khi URL regexp from Componentkhông bình luận, một số giải thích về những gì nó đang làm sẽ hữu ích. Autolinker.jsđược nhận xét rất tốt và có bài kiểm tra. Các urlize.jsthư viện liên kết đến trong câu trả lời Vebjorn Ljosa của cũng trông featureful và duy trì tốt, mặc dù nó không có bài kiểm tra.
Sam Hasler

1
Regex101.com tự động "giải thích" regrec , nhưng chúc may mắn với điều đó :) Tôi cũng đã nhanh chóng tìm thấy một trường hợp thất bại với TLD không hợp lệ (cùng liên kết).
Dan Dascalescu

1
@SamHasler: Autolinker cần cải thiện trong khu vực TLD và IDN. Đã thêm một số bài kiểm tra .
Dan Dascalescu

2
Tò mò rằng không ai đề cập đến những nỗ lực của John Gruber trong việc duy trì mẫu biểu thức chính thức URL . Đó không phải là giải pháp duy nhất / lý tưởng cho vấn đề, nhưng trong mọi trường hợp đáng để điều tra, nếu bạn đang đưa ra giải pháp của riêng mình. Chỉ muốn thêm điều này như một tài liệu tham khảo.
oelna

2
@DanDascalescu Hãy xem markdown-it.github.io/linkify-it . Thư viện này được tập trung chính xác vào một nhiệm vụ - phát hiện các mẫu liên kết trong văn bản. Nhưng tôi hy vọng, nó làm điều đó tốt. Ví dụ, nó có hỗ trợ unicode chính xác, bao gồm các ký tự Astral. Và nó hỗ trợ TLD quốc tế.
Vitaly

285

Thay thế URL bằng các liên kết (Trả lời cho vấn đề chung)

Các biểu thức chính quy trong câu hỏi bỏ lỡ rất nhiều trường hợp cạnh. Khi phát hiện URL, tốt hơn hết là sử dụng một thư viện chuyên xử lý các tên miền quốc tế, các TLD mới như .museum, dấu ngoặc đơn và các dấu câu khác trong và cuối URL và nhiều trường hợp cạnh khác. Xem bài đăng trên blog của Jeff Atwood Vấn đề với URL để được giải thích về một số vấn đề khác.

Các tóm tắt tốt nhất của thư viện phù hợp với URL là trong trả lời Dan Dascalescu của+100
(tính đến tháng 2 năm 2014)


"Tạo biểu thức chính quy thay thế nhiều hơn một kết quả khớp" (Trả lời cho vấn đề cụ thể)

Thêm "g" vào cuối biểu thức chính quy để cho phép kết hợp toàn cục:

/ig;

Nhưng điều đó chỉ khắc phục vấn đề trong câu hỏi trong đó biểu thức chính quy chỉ thay thế cho trận đấu đầu tiên. Không sử dụng mã đó.


150

Tôi đã thực hiện một số sửa đổi nhỏ đối với mã của Travis (chỉ để tránh mọi sự khai báo không cần thiết - nhưng nó hoạt động rất tốt cho nhu cầu của tôi, vì vậy công việc rất tuyệt!):

function linkify(inputText) {
    var replacedText, replacePattern1, replacePattern2, replacePattern3;

    //URLs starting with http://, https://, or ftp://
    replacePattern1 = /(\b(https?|ftp):\/\/[-A-Z0-9+&@#\/%?=~_|!:,.;]*[-A-Z0-9+&@#\/%=~_|])/gim;
    replacedText = inputText.replace(replacePattern1, '<a href="$1" target="_blank">$1</a>');

    //URLs starting with "www." (without // before it, or it'd re-link the ones done above).
    replacePattern2 = /(^|[^\/])(www\.[\S]+(\b|$))/gim;
    replacedText = replacedText.replace(replacePattern2, '$1<a href="http://$2" target="_blank">$2</a>');

    //Change email addresses to mailto:: links.
    replacePattern3 = /(([a-zA-Z0-9\-\_\.])+@[a-zA-Z\_]+?(\.[a-zA-Z]{2,6})+)/gim;
    replacedText = replacedText.replace(replacePattern3, '<a href="mailto:$1">$1</a>');

    return replacedText;
}

1
Làm cách nào để chỉnh sửa mã này để không làm hại các đối tượng nhúng và iframe .. (đối tượng nhúng youtube và iframe)
Pradyut Bhattacharya

5
Có một lỗi trong mã phù hợp với địa chỉ email ở đây. [a-zA-Z]{2,6}nên đọc một cái gì đó dọc theo dòng (?:[a-zA-Z]{2,6})+để khớp với các tên miền phức tạp hơn, ví dụ email@example.co.uk.
Roshambo

1
Tôi đã gặp phải một số vấn đề; đầu tiên chỉ cần http: // hoặc http: // www (không có không gian www thậm chí SO phân tích lỗi này rõ ràng) sẽ tạo ra một liên kết. Và liên kết với http: // www. miền . com (không có dấu cách) sẽ tạo một liên kết trống và sau đó một liên kết với thẻ đóng neo được đính kèm trong trường href.
Alfred

1
Điều gì về URL mà không có http://hoặc www? Điều này sẽ làm việc cho các loại URL?
Nathan

2
Tôi đã cố gắng chỉnh sửa bài đăng gốc để khắc phục sự cố mailto, nhưng tôi phải thêm ít nhất 6 ký tự để thực hiện chỉnh sửa. Nhưng nếu bạn thay đổi dòng này: replacePattern3 = /(\w+@[a-zA-Z_]+?\.[a-zA-Z]{2,6})/gim;với cách replacePattern3 = /(\w+@[a-zA-Z_]+?(\.[a-zA-Z]{2,6})+)/gim;khắc phục sự cố mail này :)
triển của bạn vào

70

Thực hiện một số tối ưu hóa cho Linkify()mã của Travis ở trên. Tôi cũng đã sửa một lỗi trong đó các địa chỉ email có định dạng loại tên miền phụ sẽ không được khớp (ví dụ example@domain.co.uk).

Ngoài ra, tôi đã thay đổi việc triển khai thành nguyên mẫu Stringlớp để các mục có thể được khớp như vậy:

var text = 'address@example.com';
text.linkify();

'http://stackoverflow.com/'.linkify();

Dù sao, đây là kịch bản:

if(!String.linkify) {
    String.prototype.linkify = function() {

        // http://, https://, ftp://
        var urlPattern = /\b(?:https?|ftp):\/\/[a-z0-9-+&@#\/%?=~_|!:,.;]*[a-z0-9-+&@#\/%=~_|]/gim;

        // www. sans http:// or https://
        var pseudoUrlPattern = /(^|[^\/])(www\.[\S]+(\b|$))/gim;

        // Email addresses
        var emailAddressPattern = /[\w.]+@[a-zA-Z_-]+?(?:\.[a-zA-Z]{2,6})+/gim;

        return this
            .replace(urlPattern, '<a href="$&">$&</a>')
            .replace(pseudoUrlPattern, '$1<a href="http://$2">$2</a>')
            .replace(emailAddressPattern, '<a href="mailto:$&">$&</a>');
    };
}

Theo ý kiến ​​của tôi, tốt nhất, vì các chức năng Prototype làm cho mọi thứ sạch sẽ hơn rất nhiều :)
MRVDOG

có vẻ như nó không hoạt động với các địa chỉ email như vậy: info@some-thing.com some.thing@example.com vv ..
Marco Gagliardi

@MarcoGagliardi Bắt tốt. Đã sửa.
Roshambo

1
Điều này không hoạt động đối với chuỗi "git clone aaaa@bitbucket.org/ooo/bbb-cc-dd.git ". Nó đã phá vỡ chuỗi thành các khối và tạo ra nhiều neo như thế này "git clone <a href="https://<a href="mailto:aaaa@bitbucket.org"> aaaa@bitbucket.org </a> / ooo / bbb-cc-dd.git "> https: // <a href="mailto:aaaa@bitbucket.org"> aaaa@bitbucket.org </a> /ooo/bbb-cc-dd.git </a> "
Jebin

1
Nó không hoạt động với +tên người dùng email, chẳng hạn như foo+bar@domain.com. Tôi đã sửa nó bằng mẫu email /[\w.+]+@[a-zA-Z_-]+?(?:\.[a-zA-Z]{2,6})+/gim(lưu ý +trong ngoặc đầu tiên), nhưng tôi không biết liệu điều đó có phá vỡ thứ gì khác không.
dchacke

24

Cảm ơn, điều này rất hữu ích. Tôi cũng muốn một cái gì đó sẽ liên kết những thứ trông giống như một URL - như một yêu cầu cơ bản, nó sẽ liên kết một cái gì đó như www.yahoo.com, ngay cả khi không có tiền tố giao thức http: //. Về cơ bản, nếu "www." có mặt, nó sẽ liên kết nó và giả sử đó là http: //. Tôi cũng muốn email để chuyển thành mailto: links. VÍ DỤ: www.yahoo.com sẽ được chuyển đổi thành www.yahoo.com

Đây là mã tôi đã kết thúc (kết hợp mã từ trang này và các nội dung khác tôi tìm thấy trên mạng và các nội dung khác tôi đã tự làm):

function Linkify(inputText) {
    //URLs starting with http://, https://, or ftp://
    var replacePattern1 = /(\b(https?|ftp):\/\/[-A-Z0-9+&@#\/%?=~_|!:,.;]*[-A-Z0-9+&@#\/%=~_|])/gim;
    var replacedText = inputText.replace(replacePattern1, '<a href="$1" target="_blank">$1</a>');

    //URLs starting with www. (without // before it, or it'd re-link the ones done above)
    var replacePattern2 = /(^|[^\/])(www\.[\S]+(\b|$))/gim;
    var replacedText = replacedText.replace(replacePattern2, '$1<a href="http://$2" target="_blank">$2</a>');

    //Change email addresses to mailto:: links
    var replacePattern3 = /(\w+@[a-zA-Z_]+?\.[a-zA-Z]{2,6})/gim;
    var replacedText = replacedText.replace(replacePattern3, '<a href="mailto:$1">$1</a>');

    return replacedText
}

Trong phần thay thế thứ 2, phần (^ | [^ /]) chỉ thay thế www.whthing.com nếu nó chưa được thêm tiền tố // - để tránh liên kết đôi nếu một URL đã được liên kết trong lần thay thế đầu tiên. Ngoài ra, có thể www.whthing.com có ​​thể ở đầu chuỗi, đây là điều kiện "hoặc" đầu tiên trong phần regex đó.

Điều này có thể được tích hợp như một plugin jQuery như Jesse P đã minh họa ở trên - nhưng tôi đặc biệt muốn một hàm thông thường không hoạt động trên một phần tử DOM hiện có, bởi vì tôi đang lấy văn bản tôi có và sau đó thêm nó vào DOM và Tôi muốn văn bản được "liên kết" trước khi tôi thêm nó, vì vậy tôi chuyển văn bản qua chức năng này. Công trình tuyệt vời.


1
Có một vấn đề với mẫu thứ 2, hoàn toàn khớp với "www.domain.com". Vấn đề tồn tại khi url có một số loại giới thiệu trong đó, như: & location = http% 3A% 2F% 2Fwww.amazon.com% 2FNeil-Young% 2Fe% 2FB000APYJWA% 3Fqid% 3D1280679945% 26sr% 3D8 -20 & linkCode = ur2 & camp = 1789 & creative = 9325 - trong trường hợp đó, liên kết tự động liên kết lại. Cách khắc phục nhanh là thêm ký tự "f" vào sau danh sách phủ định có chứa "/". Vì vậy, biểu thức là: thay thếPotype2 = / (^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ [[S [+ (\ b | $)) / Red
Redtopia

Mã ở trên sẽ thất bại rất nhiều thử nghiệm cho các trường hợp cạnh. Khi phát hiện URL, tốt hơn là nên dựa vào một thư viện chuyên ngành. Đây là lý do tại sao .
Dan Dascalescu

2
Tôi chỉ chạy nó trên một chuỗi trong đó một số liên kết web đã có a href liên kết trên chúng. Trong trường hợp này, nó không làm hỏng các liên kết làm việc hiện có.
AdamJones

17

Xác định URL là khó khăn vì chúng thường được bao quanh bởi dấu chấm câu và vì người dùng thường không sử dụng hình thức đầy đủ của URL. Nhiều hàm JavaScript tồn tại để thay thế URL bằng siêu liên kết, nhưng tôi không thể tìm thấy một URL hoạt động cũng như urlizebộ lọc trong khung web dựa trên Python Django. Do đó, tôi đã chuyển urlizechức năng của Django sang JavaScript:

https://github.com/ljosa/urlize.js

Một ví dụ:

urlize('Go to SO (stackoverflow.com) and ask. <grin>', 
       {nofollow: true, autoescape: true})
=> "Go to SO (<a href="http://stackoverflow.com" rel="nofollow">stackoverflow.com</a>) and ask. &lt;grin&gt;"

Đối số thứ hai, nếu đúng, gây ra rel="nofollow"được chèn vào. Đối số thứ ba, nếu đúng, thoát khỏi các ký tự có ý nghĩa đặc biệt trong HTML. Xem tập tin README .


Cũng hoạt động với nguồn html như: www.web.com <a href = "https: // github. Com"> url </ a> một số văn bản
Paulius Zaliaduonis

@Paulius: nếu bạn đặt tùy chọn django_compatiblethành false, nó sẽ xử lý trường hợp sử dụng đó tốt hơn một chút.
Vebjorn Ljosa

Django urlizekhông hỗ trợ TLD đúng cách (ít nhất không phải là cổng JS trên GitHub). Một thư viện xử lý TLD đúng cáchLinkify JavaScript của Ben Alman .
Dan Dascalescu

Hỗ trợ phát hiện URL với các tên miền cấp cao bổ sung ngay cả khi URL không bắt đầu bằng "http" hoặc "www" đã được thêm.
Vebjorn Ljosa

10

Tôi đã thực hiện thay đổi đối với Roshambo String.linkify () thành emailAddressPotype để nhận ra địa chỉ aaa.bbb. @ Ccc.ddd

if(!String.linkify) {
    String.prototype.linkify = function() {

        // http://, https://, ftp://
        var urlPattern = /\b(?:https?|ftp):\/\/[a-z0-9-+&@#\/%?=~_|!:,.;]*[a-z0-9-+&@#\/%=~_|]/gim;

        // www. sans http:// or https://
        var pseudoUrlPattern = /(^|[^\/])(www\.[\S]+(\b|$))/gim;

        // Email addresses *** here I've changed the expression ***
        var emailAddressPattern = /(([a-zA-Z0-9_\-\.]+)@[a-zA-Z_]+?(?:\.[a-zA-Z]{2,6}))+/gim;

        return this
            .replace(urlPattern, '<a target="_blank" href="$&">$&</a>')
            .replace(pseudoUrlPattern, '$1<a target="_blank" href="http://$2">$2</a>')
            .replace(emailAddressPattern, '<a target="_blank" href="mailto:$1">$1</a>');
    };
}

Mã ở trên sẽ thất bại rất nhiều thử nghiệm cho các trường hợp cạnh. Khi phát hiện URL, tốt hơn là nên dựa vào một thư viện chuyên ngành. Đây là lý do tại sao .
Dan Dascalescu

9

Tôi đã tìm kiếm trên google cho bất cứ điều gì mới hơn và chạy qua cái này:

$('p').each(function(){
   $(this).html( $(this).html().replace(/((http|https|ftp):\/\/[\w?=&.\/-;#~%-]+(?![\w\s?&.\/;#~%"=-]*>))/g, '<a href="$1">$1</a> ') );
});

bản demo: http://jsfiddle.net/kachibito/hEgvc/1/

Hoạt động thực sự tốt cho các liên kết bình thường.


"Liên kết bình thường" ở đây là gì? Nhìn vào ngã ba của bản demo của bạn ở đây: jsfiddle.net/hEgvc/27 Mọi người sẽ che đậy và sẽ làm điều này một cách dễ dàng. URI không phải là điều dễ dàng theo RFC3986 và nếu bạn chỉ muốn bao gồm "Liên kết bình thường", tôi khuyên bạn nên tuân theo regrec này ít nhất: ^ (([^: /? #] +) :)? (// ([ ^ /? #] *))? ([^? #] *) (\? ([^ #] *))? (# (. *))?
Ivan

2
Tôi có nghĩa là bất cứ điều gì trong định dạng http://example.com/folder/folder/folder/hoặc https://example.org/blahvv - chỉ định dạng URL không điên điển hình của bạn sẽ phù hợp với 95-99% trường hợp sử dụng ngoài kia. Tôi đang sử dụng điều này cho một khu vực hành chính nội bộ, vì vậy tôi không cần bất cứ điều gì lạ mắt để nắm bắt các trường hợp cạnh hoặc liên kết băm.
thoái hóa


5

Giải pháp này hoạt động giống như nhiều giải pháp khác và trên thực tế sử dụng regex giống như một trong số chúng, tuy nhiên thay vì trả về Chuỗi HTML, điều này sẽ trả về một đoạn tài liệu có chứa phần tử A và bất kỳ nút văn bản có thể áp dụng nào.

 function make_link(string) {
    var words = string.split(' '),
        ret = document.createDocumentFragment();
    for (var i = 0, l = words.length; i < l; i++) {
        if (words[i].match(/[-a-zA-Z0-9@:%_\+.~#?&//=]{2,256}\.[a-z]{2,4}\b(\/[-a-zA-Z0-9@:%_\+.~#?&//=]*)?/gi)) {
            var elm = document.createElement('a');
            elm.href = words[i];
            elm.textContent = words[i];
            if (ret.childNodes.length > 0) {
                ret.lastChild.textContent += ' ';
            }
            ret.appendChild(elm);
        } else {
            if (ret.lastChild && ret.lastChild.nodeType === 3) {
                ret.lastChild.textContent += ' ' + words[i];
            } else {
                ret.appendChild(document.createTextNode(' ' + words[i]));
            }
        }
    }
    return ret;
}

Có một số cảnh báo, cụ thể là với hỗ trợ IE và textContent cũ hơn.

đây là một bản demo


2
@DanDascalescu Thay vì hạ thấp số tiền có thể cung cấp các trường hợp cạnh đã nói của bạn.
rlemon

Tôi có cần phải? Hãy xem biểu thức chính quy cho các URL . Nhưng nếu bạn khăng khăng, hãy chạy đua với bộ kiểm tra linkify của Ben Alman . Tôi đã bắt đầu đóng góp các bài kiểm tra thất bại, ví dụ như cho url hóa , nhưng sớm nhận ra rằng chỉ đáng làm như vậy cho những nỗ lực thư viện nghiêm túc. Với tất cả sự tôn trọng, câu hỏi trên là câu trả lời của StackOverflow, không phải là một thư viện có nguồn mở đang cố phân tích URL chính xác.
Dan Dascalescu

2
vì vậy có trường hợp cạnh. Tuyệt vời. những câu trả lời này vẫn có thể hữu ích cho những người khác và việc hạ thấp chúng xuống có vẻ như quá mức cần thiết. Những câu trả lời khác mà bạn đã nhận xét về và dường như downvoted làm chứa thông tin hữu ích (cũng như câu trả lời của bạn). không phải ai cũng sẽ chống lại các trường hợp đã nói, và không phải ai cũng muốn sử dụng thư viện.
rlemon

Chính xác. Những người không hiểu những hạn chế của regexps là những người sẽ vui vẻ đọc lướt bản regex đầu tiên từ câu trả lời được đánh giá cao nhất và chạy theo nó. Đó là những người nên sử dụng thư viện nhiều nhất.
Dan Dascalescu

1
Nhưng làm thế nào mà biện minh để bỏ phiếu mọi câu trả lời với regrec không phải là giải pháp ưa thích của bạn?
rlemon

4

Nếu bạn cần hiển thị liên kết ngắn hơn (chỉ tên miền), nhưng với cùng một URL dài, bạn có thể thử sửa đổi phiên bản mã của Sam Hasler đã đăng ở trên

function replaceURLWithHTMLLinks(text) {
    var exp = /(\b(https?|ftp|file):\/\/([-A-Z0-9+&@#%?=~_|!:,.;]*)([-A-Z0-9+&@#%?\/=~_|!:,.;]*)[-A-Z0-9+&@#\/%=~_|])/ig;
    return text.replace(exp, "<a href='$1' target='_blank'>$3</a>");
}

3

Reg Ex: /(\b((https?|ftp|file):\/\/|(www))[-A-Z0-9+&@#\/%?=~_|!:,.;]*[-A-Z0-9+&@#\/%=~_|]*)/ig

function UriphiMe(text) {
      var exp = /(\b((https?|ftp|file):\/\/|(www))[-A-Z0-9+&@#\/%?=~_|!:,.;]*[-A-Z0-9+&@#\/%=~_|]*)/ig; 
      return text.replace(exp,"<a href='$1'>$1</a>");
}

Dưới đây là một số chuỗi thử nghiệm:

  1. Tìm tôi trên www.google.com
  2. www
  3. Tìm tôi trên www. http://www.com
  4. Theo dõi tôi trên: http://www.querantwork.wordpress.com
  5. http://www.querantwork.wordpress.com
  6. Theo dõi tôi trên: http://www.querantwork.wordpress.com
  7. https://stackoverflow.com/users/430804/arnant

Lưu ý: Nếu bạn không muốn vượt qua wwwlà hợp lệ, chỉ cần sử dụng bên dưới reg ex: /(\b((https?|ftp|file):\/\/|(www))[-A-Z0-9+&@#\/%?=~_|!:,.;]*[-A-Z0-9+&@#\/%=~_|])/ig


Mã ở trên sẽ thất bại rất nhiều thử nghiệm cho các trường hợp cạnh. Khi phát hiện URL, tốt hơn là dựa vào thư viện chuyên ngành. Đây là lý do tại sao .
Dan Dascalescu

3

Cần lưu ý các cảnh báo về độ phức tạp của URI, nhưng câu trả lời đơn giản cho câu hỏi của bạn là:
Để thay thế mọi trận đấu, bạn cần thêm /gcờ vào cuối RegEx:
/(\b(https?|ftp|file):\/\/[-A-Z0-9+&@#\/%?=~_|!:,.;]*[-A-Z0-9+&@#\/%=~_|])/gi


3
/**
 * Convert URLs in a string to anchor buttons
 * @param {!string} string
 * @returns {!string}
 */

function URLify(string){
  var urls = string.match(/(((ftp|https?):\/\/)[\-\w@:%_\+.~#?,&\/\/=]+)/g);
  if (urls) {
    urls.forEach(function (url) {
      string = string.replace(url, '<a target="_blank" href="' + url + '">' + url + "</a>");
    });
  }
  return string.replace("(", "<br/>(");
}

ví dụ đơn giản


2

Giữ cho nó đơn giản! Nói những gì bạn không thể có, hơn là những gì bạn có thể có :)

Như đã đề cập ở trên, các URL có thể khá phức tạp, đặc biệt là sau '?' Và không phải tất cả chúng đều bắt đầu bằng 'www.' ví dụmaps.bing.com/something?key=!"£$%^*()&lat=65&lon&lon=20

Vì vậy, thay vì có một regex phức tạp sẽ không đáp ứng tất cả các trường hợp cạnh, và sẽ khó duy trì, làm thế nào về cái đơn giản hơn nhiều này, hoạt động tốt cho tôi trong thực tế.

Trận đấu

http(s):// (anything but a space)+

www. (anything but a space)+

Trong đó 'bất cứ thứ gì' [^'"<>\s] ... về cơ bản là một trận đấu tham lam, mang đến cho bạn một không gian, trích dẫn, khung góc hoặc cuối dòng

Cũng thế:

Hãy nhớ kiểm tra xem nó chưa ở định dạng URL, ví dụ: văn bản chứa href="..."hoặcsrc="..."

Thêm ref = nofollow (nếu thích hợp)

Giải pháp này không "tốt" như các thư viện đã đề cập ở trên, nhưng đơn giản hơn nhiều và hoạt động tốt trong thực tế.

if html.match( /(href)|(src)/i )) {
    return html; // text already has a hyper link in it
    }

html = html.replace( 
            /\b(https?:\/\/[^\s\(\)\'\"\<\>]+)/ig, 
            "<a ref='nofollow' href='$1'>$1</a>" 
            );

html = html.replace( 
            /\s(www\.[^\s\(\)\'\"\<\>]+)/ig, 
            "<a ref='nofollow' href='http://$1'>$1</a>" 
            );

html = html.replace( 
             /^(www\.[^\s\(\)\'\"\<\>]+)/ig, 
            "<a ref='nofollow' href='http://$1'>$1</a>" 
            );

return html;

2

Phát hiện URL chính xác với các tên miền quốc tế & hỗ trợ các ký tự không phải là chuyện nhỏ. linkify-itthư viện xây dựng regex từ nhiều điều kiện và kích thước cuối cùng là khoảng 6 kilobyte :). Nó chính xác hơn tất cả các lib, hiện được tham chiếu trong câu trả lời được chấp nhận.

Xem linkify-it demo để kiểm tra trực tiếp tất cả các trường hợp cạnh và kiểm tra các trường hợp của bạn.

Nếu bạn cần liên kết nguồn HTML, trước tiên bạn nên phân tích cú pháp và lặp lại từng mã thông báo văn bản.


1

Tôi đã viết một thư viện JavaScript khác, nó có thể tốt hơn cho bạn vì nó rất nhạy cảm với các dương tính giả ít nhất có thể, kích thước nhanh và nhỏ. Tôi hiện đang tích cực duy trì nó vì vậy vui lòng kiểm tra nó trong trang demo và xem nó sẽ hoạt động như thế nào với bạn.

liên kết: https://github.com/alexcorvi/anchorme.js


Thư viện tuyệt vời. Cảm ơn rât nhiều!
Serdar Değirmenci

0

Tôi đã phải làm ngược lại và tạo các liên kết html thành URL, nhưng tôi đã sửa đổi biểu thức chính của bạn và nó hoạt động như một cơ duyên, cảm ơn :)

var exp = /<a\s.*href=['"[(\b(https?|ftp|file):\/\/[-A-Z0-9+&@#\/%?=~_ |!:,.;] * [- A-Z0-9 + & @ # \ /% = ~ _ |]) ['"]. *>. * <\ / A> / ig;

nguồn = source.replace (exp, "$ 1");

Tôi không thấy quan điểm của regex của bạn. Nó phù hợp với mọi thứ thay thế mọi thứ với mọi thứ. Trong thực tế, mã của bạn không làm gì cả.
Chad Grant

8
Tôi đoán tôi nên chờ bình luận để cho phép mọi người hoàn thành chỉnh sửa. lấy làm tiếc.
Chad Grant

0

Phát hiện e-mail trong câu trả lời của Travitron ở trên không hoạt động với tôi, vì vậy tôi đã mở rộng / thay thế nó bằng mã sau (mã C #).

// Change e-mail addresses to mailto: links.
const RegexOptions o = RegexOptions.Multiline | RegexOptions.IgnoreCase;
const string pat3 = @"([a-zA-Z0-9_\-\.]+)@([a-zA-Z0-9_\-\.]+)\.([a-zA-Z]{2,6})";
const string rep3 = @"<a href=""mailto:$1@$2.$3"">$1@$2.$3</a>";
text = Regex.Replace(text, pat3, rep3, o);

Điều này cho phép các địa chỉ email như " Firstname.secondname@one.two.three.co.uk ".


Mã ở trên sẽ thất bại rất nhiều thử nghiệm cho các trường hợp cạnh. Khi phát hiện URL, tốt hơn là dựa vào thư viện chuyên ngành. Đây là lý do tại sao .
Dan Dascalescu

Cảm ơn, @DanDascalescu Thông thường, đó là luôn luôn tốt hơn để quá generalize.
Uwe Keim

0

Sau khi nhập từ một số nguồn, bây giờ tôi là một giải pháp hoạt động tốt. Nó phải làm với việc viết mã thay thế của riêng bạn.

Câu trả lời .

Fiddle .

function replaceURLWithHTMLLinks(text) {
    var re = /(\(.*?)?\b((?:https?|ftp|file):\/\/[-a-z0-9+&@#\/%?=~_()|!:,.;]*[-a-z0-9+&@#\/%=~_()|])/ig;
    return text.replace(re, function(match, lParens, url) {
        var rParens = '';
        lParens = lParens || '';

        // Try to strip the same number of right parens from url
        // as there are left parens.  Here, lParenCounter must be
        // a RegExp object.  You cannot use a literal
        //     while (/\(/g.exec(lParens)) { ... }
        // because an object is needed to store the lastIndex state.
        var lParenCounter = /\(/g;
        while (lParenCounter.exec(lParens)) {
            var m;
            // We want m[1] to be greedy, unless a period precedes the
            // right parenthesis.  These tests cannot be simplified as
            //     /(.*)(\.?\).*)/.exec(url)
            // because if (.*) is greedy then \.? never gets a chance.
            if (m = /(.*)(\.\).*)/.exec(url) ||
                    /(.*)(\).*)/.exec(url)) {
                url = m[1];
                rParens = m[2] + rParens;
            }
        }
        return lParens + "<a href='" + url + "'>" + url + "</a>" + rParens;
    });
}

2
Đoạn mã trên (và hầu hết các biểu thức chính quy nói chung) sẽ thất bại rất nhiều bài kiểm tra cho các trường hợp cạnh. Khi phát hiện URL, tốt hơn là nên dựa vào một thư viện chuyên ngành. Đây là lý do tại sao .
Dan Dascalescu

Dan, Có một thư viện như vậy? Mặc dù trong trường hợp này, chúng tôi vẫn khớp với biểu thức chính quy ở trên để mã không bao giờ có thể xuất rác khi có thứ gì đó như rác (ngay cả khi thư viện khác xác nhận rác là URL / URI hợp lệ) được sử dụng làm đầu vào.
Mike Mestnik


0

Đây là giải pháp của tôi:

var content = "Visit https://wwww.google.com or watch this video: https://www.youtube.com/watch?v=0T4DQYgsazo and news at http://www.bbc.com";
content = replaceUrlsWithLinks(content, "http://");
content = replaceUrlsWithLinks(content, "https://");

function replaceUrlsWithLinks(content, protocol) {
    var startPos = 0;
    var s = 0;

    while (s < content.length) {
        startPos = content.indexOf(protocol, s);

        if (startPos < 0)
            return content;

        let endPos = content.indexOf(" ", startPos + 1);

        if (endPos < 0)
            endPos = content.length;

        let url = content.substr(startPos, endPos - startPos);

        if (url.endsWith(".") || url.endsWith("?") || url.endsWith(",")) {
            url = url.substr(0, url.length - 1);
            endPos--;
        }

        if (ROOTNS.utils.stringsHelper.validUrl(url)) {
            let link = "<a href='" + url + "'>" + url + "</a>";
            content = content.substr(0, startPos) + link + content.substr(endPos);
            s = startPos + link.length;
        } else {
            s = endPos + 1;
        }
    }

    return content;
}

function validUrl(url) {
    try {
        new URL(url);
        return true;
    } catch (e) {
        return false;
    }
}

0

Hãy thử chức năng dưới đây:

function anchorify(text){
  var exp = /(\b(https?|ftp|file):\/\/[-A-Z0-9+&@#\/%?=~_|!:,.;]*[-A-Z0-9+&@#\/%=~_|])/ig;
  var text1=text.replace(exp, "<a href='$1'>$1</a>");
  var exp2 =/(^|[^\/])(www\.[\S]+(\b|$))/gim;
  return text1.replace(exp2, '$1<a target="_blank" href="http://$2">$2</a>');
}

alert(anchorify("Hola amigo! https://www.sharda.ac.in/academics/"));


0

Thử dưới đây Giải pháp

function replaceLinkClickableLink(url = '') {
let pattern = new RegExp('^(https?:\\/\\/)?'+
        '((([a-z\\d]([a-z\\d-]*[a-z\\d])*)\\.?)+[a-z]{2,}|'+
        '((\\d{1,3}\\.){3}\\d{1,3}))'+
        '(\\:\\d+)?(\\/[-a-z\\d%_.~+]*)*'+
        '(\\?[;&a-z\\d%_.~+=-]*)?'+
        '(\\#[-a-z\\d_]*)?$','i');

let isUrl = pattern.test(url);
if (isUrl) {
    return `<a href="${url}" target="_blank">${url}</a>`;
}
return url;
}
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.