จะแทนที่ URL ธรรมดาด้วยลิงค์ได้อย่างไร


454

ฉันใช้ฟังก์ชันด้านล่างเพื่อจับคู่ URL ภายในข้อความที่กำหนดและแทนที่เป็นลิงก์ HTML นิพจน์ทั่วไปใช้งานได้ดี แต่ขณะนี้ฉันเปลี่ยนเฉพาะนัดแรกเท่านั้น

ฉันจะแทนที่ URL ทั้งหมดได้อย่างไร ฉันเดาว่าฉันควรจะใช้คำสั่งexecแต่ฉันไม่ได้คิดวิธีการทำ

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>"); 
}

คำตอบ:


351

ออกก่อนกลิ้ง regexp ของคุณเองไปยัง URL แยกเป็นความคิดที่น่ากลัว คุณต้องคิดนี้เป็นปัญหาที่พบบ่อยพอที่จะมีคนเขียน, บั๊กและทดสอบห้องสมุดให้มันเป็นไปตามRFCs URI ที่มีความซับซ้อน - ตรวจสอบสำหรับการแยก URL ใน Node.jsและหน้าวิกิพีเดียในรูปแบบ URI

มีกรณีขอบจำนวนมากเมื่อพูดถึงการแยก URL: ชื่อโดเมนสากล , จริง ( .museum) และไม่มีอยู่ ( .etc) TLDs, เครื่องหมายวรรคตอนแปลกรวมถึงวงเล็บ , เครื่องหมายวรรคตอนท้าย URL, ชื่อโฮสต์ IPV6 เป็นต้น

ฉันมองไปที่ตันของห้องสมุดและมีมูลค่าไม่กี่ใช้แม้จะมีข้อเสียบางอย่าง

ห้องสมุดที่ฉันขาดคุณสมบัติอย่างรวดเร็วสำหรับงานนี้:

หากคุณยืนยันในการแสดงออกปกติที่ครอบคลุมมากที่สุดคือURL regexp จากตัวแทนแม้ว่ามันจะตรวจจับเท็จ TLDs สองตัวอักษรที่ไม่มีอยู่จริงโดยดูที่มัน


3
เป็นเรื่องน่าเสียดายที่URL regexp from Componentไม่มีคนให้ความเห็นคำอธิบายเกี่ยวกับสิ่งที่กำลังทำอยู่จะเป็นประโยชน์ Autolinker.jsมีความคิดเห็นดีมากและมีการทดสอบ urlize.jsห้องสมุดเชื่อมโยงกับใน คำตอบของVebjørn Ljosaยังมีลักษณะ featureful และบำรุงรักษาอย่างดีแม้ว่ามันจะไม่ได้มีการทดสอบ
— Sam Hasler

1
Regex101.com "อธิบาย" regexp โดยอัตโนมัติแต่ขอให้โชคดี :) ฉันได้พบกรณีความล้มเหลวอย่างรวดเร็วด้วย TLD ที่ไม่ถูกต้อง (ลิงค์เดียวกัน)
— Dan Dascalescu

1
@SamHasler: Autolinker จำเป็นต้องปรับปรุงในพื้นที่ TLDs และ IDNs เพิ่มการทดสอบบางอย่าง
— Dan Dascalescu

2
อยากรู้อยากเห็นว่าไม่มีใครกล่าวถึงความพยายามของจอห์นกรูเบอร์ในการรักษารูปแบบ URL regex มันไม่ได้เป็นทางออกที่ดีเลิศสำหรับปัญหา แต่ในกรณีใด ๆ ที่ควรตรวจสอบหากคุณกำลังนำเสนอโซลูชันของคุณเอง ต้องการเพิ่มสิ่งนี้เป็นข้อมูลอ้างอิง
— oelna

2
@DanDascalescu ลองดูที่markdown-it.github.io/linkify-itนี้ ไลบรารี่นี้มุ่งเน้นที่งานเดียว - ตรวจจับรูปแบบลิงก์ในข้อความ แต่ฉันหวังว่ามันจะทำได้ดี ตัวอย่างเช่นมันมีการสนับสนุนยูนิโค้ดที่ถูกต้องรวมถึงตัวละครดาว และรองรับ TLD ระหว่างประเทศ
— Vitaly

285

การแทนที่ URL ด้วยลิงก์ (ตอบปัญหาทั่วไป)

การแสดงออกปกติในคำถามคิดถึงกรณีขอบจำนวนมาก เมื่อตรวจสอบ URL จะดีกว่าเสมอหากใช้ไลบรารีเฉพาะที่จัดการชื่อโดเมนต่างประเทศ TLD ใหม่เช่น.museumวงเล็บและเครื่องหมายวรรคตอนอื่น ๆ ภายในและท้าย URL และกรณีขอบอื่น ๆ ดูโพสต์บล็อกของ Jeff Atwood ปัญหากับ URLสำหรับคำอธิบายของปัญหาอื่น ๆ

สรุปที่ดีที่สุดของ URL ห้องสมุดการจับคู่อยู่ในคำตอบของแดนดาสคเลสุ100
( ณ กุมภาพันธ์ 2014)


"สร้างนิพจน์ทั่วไปแทนที่การแข่งขันมากกว่าหนึ่งรายการ" (ตอบปัญหาเฉพาะ)

เพิ่ม "g" ที่ท้ายนิพจน์ทั่วไปเพื่อเปิดใช้งานการจับคู่แบบโกลบอล:

/ig;

แต่นั่นจะแก้ไขปัญหาในคำถามที่นิพจน์ทั่วไปแทนที่การจับคู่แรกเท่านั้น อย่าใช้รหัสนั้น


150

ฉันได้ทำการปรับเปลี่ยนเล็กน้อยสำหรับรหัสของ Travis (เพื่อหลีกเลี่ยงการเผยแพร่ซ้ำที่ไม่จำเป็น - แต่มันทำงานได้ดีสำหรับความต้องการของฉันงานดีมาก!):

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
แก้ไขรหัสนี้อย่างไรเพื่อไม่ให้เป็นอันตรายต่อวัตถุและ iframes ที่ฝังตัว .. (youtube วัตถุที่ฝังและ iframes)
— Pradyut Bhattacharya

5
มีข้อบกพร่องในรหัสที่ตรงกับที่อยู่อีเมลที่นี่ [a-zA-Z]{2,6}ควรอ่านบางอย่างตาม(?:[a-zA-Z]{2,6})+ลำดับเพื่อให้ตรงกับชื่อโดเมนที่ซับซ้อนมากขึ้นเช่น email@example.co.uk
— Roshambo

1
ฉันพบปัญหาบางอย่าง; ก่อนอื่นเพียงแค่ http: // หรือ http: // www (โดยไม่ต้องเว้นวรรคwwwดังนั้นจะแยกวิเคราะห์ความผิดพลาดนี้อย่างเห็นได้ชัด) จะสร้างลิงก์ และลิงก์ด้วย http: // www โดเมน com (ไม่มีช่องว่าง) จะสร้างลิงก์ว่างเปล่าหนึ่งลิงก์และอีกหนึ่งลิงก์ที่มีแท็กปิดการยึดสมอที่แนบในฟิลด์ href
— อัลเฟรด

1
สิ่งที่เกี่ยวกับ URL ที่โดยไม่ต้องhttp://หรือwww? สิ่งนี้จะใช้ได้กับ URL ประเภทนั้นหรือไม่
— นาธัน

2
ฉันพยายามแก้ไขโพสต์ดั้งเดิมเพื่อแก้ไขปัญหา mailto แต่ฉันต้องเพิ่มอย่างน้อย 6 ตัวอักษรเพื่อทำการแก้ไข แต่ถ้าคุณเปลี่ยนบรรทัดนี้: replacePattern3 = /(\w+@[a-zA-Z_]+?\.[a-zA-Z]{2,6})/gim;ด้วยสิ่งนี้replacePattern3 = /(\w+@[a-zA-Z_]+?(\.[a-zA-Z]{2,6})+)/gim;ที่ช่วยแก้ปัญหา mailto :)
— yourdeveloperfriend

70

ทำการเพิ่มประสิทธิภาพบางอย่างกับLinkify()รหัสของเทรวิสข้างต้น ฉันยังแก้ไขข้อบกพร่องที่ที่อยู่อีเมลที่มีรูปแบบประเภทโดเมนย่อยจะไม่ถูกจับคู่ (เช่น example@domain.co.uk)

นอกจากนี้ฉันเปลี่ยนการใช้งานเป็นต้นแบบของStringคลาสเพื่อให้สามารถจับคู่รายการดังนี้:

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

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

อย่างไรก็ตามนี่คือสคริปต์:

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>');
    };
}

ที่ดีที่สุดในความคิดของฉันในขณะที่ฟังก์ชั่น Prototype ทำให้ทุกอย่างดูสะอาดตาขึ้นมาก :)
— MRVDOG

ดูเหมือนว่าจะใช้งานไม่ได้กับที่อยู่อีเมลดังกล่าว: info@some-thing.com some.thing@example.com ฯลฯ ..
— Marco Gagliardi

@MarcoGagliardi จับดี แก้ไขแล้ว.
— Roshambo

1
สิ่งนี้ใช้ไม่ได้กับสตริง "git clone aaaa@bitbucket.org/ooo/bbb-cc-dd.git " มันแบ่งสตริงออกเป็นชิ้น ๆ และสร้างจุดยึดหลายแบบเช่น "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
มันไม่ได้ทำงานกับในชื่อผู้ใช้อีเมลเช่น+ foo+bar@domain.comฉันแก้ไขด้วยรูปแบบอีเมล/[\w.+]+@[a-zA-Z_-]+?(?:\.[a-zA-Z]{2,6})+/gim(สังเกต+ในวงเล็บแรก) แต่ฉันไม่รู้ว่าสิ่งอื่นแตกต่างหรือไม่
— dchacke

24

ขอบคุณสิ่งนี้มีประโยชน์มาก ฉันต้องการสิ่งที่จะเชื่อมโยงสิ่งต่าง ๆ ที่ดูเหมือน URL - เป็นข้อกำหนดพื้นฐานมันจะเชื่อมโยงบางอย่างเช่น www.yahoo.com แม้ว่าจะไม่มีคำนำหน้าโปรโตคอล http: // โดยพื้นฐานแล้วถ้า "www." มีอยู่แล้วมันจะเชื่อมโยงและถือว่าเป็น http: // ฉันต้องการให้อีเมลเปลี่ยนเป็น mailto: ลิงก์ด้วย ตัวอย่าง: www.yahoo.com จะถูกแปลงเป็น www.yahoo.com

นี่คือรหัสที่ฉันได้รับ (ด้วยการรวมกันของรหัสจากหน้านี้และสิ่งอื่น ๆ ที่ฉันพบออนไลน์และสิ่งอื่น ๆ ที่ฉันทำด้วยตัวเอง):

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
}

ในการแทนที่ครั้งที่ 2 ส่วน (^ | [^ /]) จะแทนที่ www.whething.com เท่านั้นหากยังไม่ได้ใส่คำนำหน้าด้วย // - เพื่อหลีกเลี่ยงการเชื่อมโยงสองครั้งหาก URL นั้นถูกเชื่อมโยงในการแทนที่ครั้งแรกแล้ว นอกจากนี้ยังเป็นไปได้ว่า www.whever.com อาจอยู่ที่จุดเริ่มต้นของสตริงซึ่งเป็นเงื่อนไข "หรือ" แรกในส่วนของ regex

สิ่งนี้สามารถรวมเป็นปลั๊กอิน jQuery ตามที่ Jesse P แสดงไว้ด้านบน - แต่ฉันต้องการฟังก์ชั่นปกติที่ไม่ได้ทำหน้าที่องค์ประกอบ DOM ที่มีอยู่เพราะฉันรับข้อความที่ฉันมีแล้วเพิ่มลงใน DOM และ ฉันต้องการให้ข้อความเป็น "linkified" ก่อนที่จะเพิ่มดังนั้นฉันจึงส่งข้อความผ่านฟังก์ชั่นนี้ ใช้งานได้ดี


1
มีปัญหากับรูปแบบที่ 2 ซึ่งตรงกับ "www.domain.com" ล้วนๆด้วยตัวเอง ปัญหาเกิดขึ้นเมื่อ url มีผู้อ้างอิงอยู่ในนั้นเช่น: & location = http% 3A% 2F% 2Fwww.amazon.com% 2FNeil-Young% 2Fe% 2FB000APYJWA% 3Fqid% 3D1280679945% 26sr% 3D8-2-ent & tag = tra0c7 -20 & linkCode = ur2 & camp = 1789 & creative = 9325 - ในกรณีนี้ลิงก์อัตโนมัติลิงก์อีกครั้ง การแก้ไขอย่างรวดเร็วคือการเพิ่มตัวอักษร "f" หลังจากรายการเมื่อตะกี้ที่มี "/" ดังนั้นการแสดงออกคือ: replacePattern2 = /(^^^^\/flike)(www\.ه\Sเหมือนกัน +(\b|$))/gim
— Redtopia

รหัสด้านบนจะล้มเหลวในการทดสอบจำนวนมากสำหรับเคสแบบมีขอบ เมื่อตรวจจับ URL จะดีกว่าหากใช้ไลบรารีเฉพาะ นี่คือเหตุผลที่
— Dan Dascalescu

2
ฉันเพิ่งวิ่งไปตามสายอักขระที่บางเว็บลิงค์มี href ลิงก์อยู่แล้ว ในกรณีนี้มันล้มเหลวในการเชื่อมโยงการทำงานที่มีอยู่
— AdamJones

17

การระบุ URL นั้นยุ่งยากเนื่องจากมักถูกล้อมรอบด้วยเครื่องหมายวรรคตอนและเนื่องจากผู้ใช้มักไม่ใช้ URL แบบเต็มรูปแบบ มีฟังก์ชัน JavaScript จำนวนมากสำหรับการแทนที่ URL ด้วยการเชื่อมโยงหลายมิติ แต่ฉันไม่พบฟังก์ชันที่ทำงานได้ดีรวมถึงurlizeตัวกรองใน Django ที่ใช้เฟรมเวิร์กบน Python ดังนั้นฉันจึงเปลี่ยนurlizeฟังก์ชั่นของ Django เป็นJavaScript:

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

ตัวอย่าง:

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;"

อาร์กิวเมนต์ที่สองหากเป็นจริงrel="nofollow"จะทำให้เกิดการแทรก อาร์กิวเมนต์ที่สามถ้าเป็นจริงจะหนีอักขระที่มีความหมายพิเศษใน HTML ดูแฟ้ม Readme


ทำงานร่วมกับซอร์ส HTML เช่น: www.web.com <a href = "https: // github. com"> url </ a> ข้อความบางข้อความ
— Paulius Zaliaduonis

@ Paulius: หากคุณตั้งค่าตัวเลือกdjango_compatibleเป็นเท็จมันจะจัดการกับกรณีการใช้งานที่ดีขึ้นเล็กน้อย
— Vebjorn Ljosa

Django urlizeไม่รองรับ TLD อย่างถูกต้อง (อย่างน้อยไม่ใช่พอร์ต JS บน GitHub) ห้องสมุดที่จับ TLDs ถูกต้องคือเบน Alman ของ JavaScript Linkify
— Dan Dascalescu

รองรับการตรวจจับ URL ด้วยโดเมนระดับบนสุดเพิ่มเติมแม้ว่า URL จะไม่ขึ้นต้นด้วย "http" หรือ "www"
— Vebjorn Ljosa

10

ฉันเปลี่ยน Roshambo String.linkify () เป็น emailAddressPattern เพื่อให้รู้จัก 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>');
    };
}

รหัสด้านบนจะล้มเหลวในการทดสอบจำนวนมากสำหรับเคสแบบมีขอบ เมื่อตรวจจับ URL จะดีกว่าหากใช้ไลบรารีเฉพาะ นี่คือเหตุผลที่
— Dan Dascalescu

9

ฉันค้นหาใน google เพื่อหาสิ่งใหม่และวิ่งข้ามอันนี้:

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

ตัวอย่าง: http://jsfiddle.net/kachibito/hEgvc/1/

ทำงานได้ดีจริงๆสำหรับลิงก์ปกติ


"ลิงก์ปกติ" ที่นี่คืออะไร ดูตัวอย่างการสาธิตของคุณที่นี่: jsfiddle.net/hEgvc/27ผู้คนจะปิดบังการเปิดเผยและจะทำให้เป็นเรื่องง่าย URI ไม่ใช่เรื่องง่ายตาม RFC3986 และหากคุณต้องการที่จะครอบคลุม "ลิงก์ปกติ" เท่านั้นฉันแนะนำให้ทำตาม regexp นี้อย่างน้อย: ^ (([^: /? #] +):)? (// ([ ^ /? #] *))? ([^? #] *) (\? ([^ #] *))? (# (. *))?
— Ivan

2
ฉันหมายถึงอะไรในรูปแบบhttp://example.com/folder/folder/folder/หรือhttps://example.org/blahอื่น ๆ - เพียงแค่รูปแบบ URL ที่ไม่บ้าทั่วไปของคุณที่จะตรงกับ 95-99% ของกรณีการใช้งานออกมี ฉันกำลังใช้สิ่งนี้สำหรับเขตการปกครองภายในดังนั้นฉันไม่ต้องการอะไรที่แปลกใหม่สำหรับการจับตัวกล่องหรือ hashlinks
— เสื่อมโทรม


5

โซลูชันนี้ใช้งานได้เหมือนคนอื่น ๆ และในความเป็นจริงจะใช้ regex เดียวกับหนึ่งในนั้น แต่ในทางกลับกันสตริง HTML จะส่งคืนชิ้นส่วนเอกสารที่มีองค์ประกอบ A และโหนดข้อความใด ๆ ที่เกี่ยวข้อง

 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;
}

มีข้อควรระวังบางประการเช่น IE ที่เก่ากว่าและการสนับสนุน textContent

นี่คือตัวอย่าง


2
@DanDascalescu แทนผ้าห่ม downvoting มากอาจให้กรณีขอบของคุณกล่าวว่า
— rlemon

ฉันจำเป็นต้อง ลองดูที่ที่regexp ตัวแทนสำหรับ URL แต่ถ้าคุณยืนยันทำงานกับชุดทดสอบ linkify เบน Alman ของ ฉันไม่เริ่มต้นการมีส่วนร่วมในการทดสอบล้มเหลวเช่นการurlizeแต่รู้ทันทีว่ามันคุ้มค่าในการทำเช่นนั้นเพียงสำหรับความพยายามของห้องสมุดร้ายแรง ด้วยความเคารพเนื่องจากสิ่งที่กล่าวมาข้างต้นคือคำตอบของ StackOverflow ไม่ใช่ไลบรารีที่เปิดแหล่งที่มาซึ่งพยายามแยกวิเคราะห์ URL อย่างถูกต้อง
— Dan Dascalescu

2
ดังนั้นจึงมีกรณีขอบ ยอดเยี่ยม คำตอบเหล่านี้ยังอาจเป็นประโยชน์กับผู้อื่นและครอบคลุมการ downvoting พวกเขาดูเหมือน overkill คำตอบอื่น ๆ ที่คุณแสดงความคิดเห็นและ downvote ดูเหมือนจะมีข้อมูลที่เป็นประโยชน์ (เช่นเดียวกับคำตอบของคุณ) ไม่ใช่ทุกคนที่จะมาคัดค้านคดีดังกล่าวและทุกคนจะไม่ต้องการใช้ห้องสมุด
— rlemon

เผง ผู้ที่ไม่เข้าใจข้อ จำกัด ของ regexps คือผู้ที่จะเรียด regexp แรกอย่างมีความสุขจากคำตอบ upvoted ที่สุดและทำงานกับมัน นี่คือคนที่ควรใช้ห้องสมุดมากที่สุด
— Dan Dascalescu

1
แต่เหตุผลที่ลงคะแนนเสียงทุกคำตอบกับ regexp ไม่ใช่โซลูชั่นที่คุณต้องการ?
— rlemon

4

หากคุณต้องการแสดงลิงค์ที่สั้นกว่า (เฉพาะโดเมน) แต่มี URL ที่ยาวเหมือนกันคุณสามารถลองแก้ไขโค้ดของ Sam Hasler ที่โพสต์ไว้ด้านบน

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 อดีต: /(\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>");
}

ด้านล่างนี้เป็นสตริงที่ทดสอบ:

  1. พบกับฉันได้ที่ www.google.com
  2. www
  3. หาฉันที่ www http://www.com
  4. ติดตามฉันได้ที่: http://www.nishantwork.wordpress.com
  5. http://www.nishantwork.wordpress.com
  6. ติดตามฉันได้ที่: http://www.nishantwork.wordpress.com
  7. https://stackoverflow.com/users/430803/nishant

หมายเหตุ: หากคุณไม่ต้องการผ่านwwwอย่างถูกต้องให้ใช้ด้านล่าง reg ex: /(\b((https?|ftp|file):\/\/|(www))[-A-Z0-9+&@#\/%?=~_|!:,.;]*[-A-Z0-9+&@#\/%=~_|])/ig


รหัสด้านบนจะล้มเหลวในการทดสอบจำนวนมากสำหรับเคสแบบมีขอบ เมื่อตรวจจับ URL จะดีกว่าหากใช้ไลบรารีเฉพาะ นี่คือเหตุผลที่
— Dan Dascalescu

3

คำเตือนเกี่ยวกับความซับซ้อนของ URI นั้นควรได้รับการจดบันทึก แต่คำตอบง่ายๆสำหรับคำถามของคุณคือ:
ในการแทนที่การแข่งขันทุกครั้งคุณต้องเพิ่มการ/gตั้งค่าสถานะที่ส่วนท้ายของ 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/>(");
}

ตัวอย่างง่ายๆ


2

ง่าย ๆ เข้าไว้! พูดในสิ่งที่คุณไม่สามารถทำได้แทนที่จะเป็นอะไรก็ได้ :)

ดังที่ได้กล่าวมาแล้ว URL สามารถค่อนข้างซับซ้อนโดยเฉพาะอย่างยิ่งหลังจาก '?' และไม่ใช่ทั้งหมดเริ่มต้นด้วย 'www' เช่นmaps.bing.com/something?key=!"£$%^*()&lat=65&lon&lon=20

ดังนั้นแทนที่จะมี regex ที่ซับซ้อนที่จะไม่เจอเคสขอบทั้งหมดและจะยากที่จะรักษาวิธีนี้ง่ายกว่ามากซึ่งใช้งานได้ดีสำหรับฉันในทางปฏิบัติ

การจับคู่

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

www. (anything but a space)+

โดยที่ 'อะไรก็ได้' คือ[^'"<>\s] ... โดยทั่วไปเป็นการจับคู่ที่โลภจับคุณไปพบกับพื้นที่พูดคำพูดวงเล็บมุมหรือจุดสิ้นสุดของบรรทัด

นอกจากนี้:

อย่าลืมตรวจสอบว่ามันไม่ได้อยู่ในรูปแบบ URL เช่นข้อความมีhref="..."หรือsrc="..."

เพิ่ม ref = nofollow (ถ้าเหมาะสม)

วิธีแก้ปัญหานี้ไม่ได้ "ดี" ตามที่กล่าวไว้ข้างต้น แต่ง่ายกว่ามากและใช้งานได้ดีในทางปฏิบัติ

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

การตรวจสอบ URL ที่ถูกต้องด้วยโดเมนระหว่างประเทศและการรองรับตัวอักษรแบบดาวไม่ใช่สิ่งเล็กน้อย linkify-itLibrary สร้าง regex จากหลายเงื่อนไขและขนาดสุดท้ายคือประมาณ 6 กิโลไบต์ :) มันแม่นยำมากกว่า libs ทั้งหมดซึ่งอ้างอิงในคำตอบที่ยอมรับ

ดูการสาธิต linkify-itเพื่อตรวจสอบเคสสุดสดและทดสอบเคสของคุณ

หากคุณต้องการเชื่อมโยงแหล่งที่มาของ HTML คุณควรแยกมันก่อนแล้ววนซ้ำแต่ละโทเค็นข้อความแยกต่างหาก


1

ฉันได้เขียนไลบรารี JavaScript อีกชุดหนึ่งแล้วมันอาจจะดีกว่าสำหรับคุณเนื่องจากมีความอ่อนไหวมากกับผลบวกปลอมที่เป็นไปได้น้อยที่สุดรวดเร็วและขนาดเล็ก ฉันกำลังทำการบำรุงรักษาอยู่ในขณะนี้ดังนั้นโปรดทดสอบในหน้าสาธิตและดูว่ามันจะทำงานอย่างไรสำหรับคุณ

ลิงก์: https://github.com/alexcorvi/anchorme.js


ห้องสมุดที่น่ากลัว ขอบคุณมาก!
— Serdar Değirmenci

0

ฉันต้องทำในสิ่งที่ตรงกันข้ามและสร้างลิงก์ html เป็นเพียง URL แต่ฉันได้แก้ไข regex ของคุณและใช้งานได้อย่างมีเสน่ห์ขอบคุณ :)

var exp = /<a\s.*href= [ดีกว่า "\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\ & ทาง: ด้าน & = & nbsp; |:...;] * [- A-Z0-9 + & @ # \ /% = ~ _ |]) [ "] *> * <\ / a> / ig;

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

ฉันไม่เห็นจุด regex ของคุณ มันตรงกับทุกสิ่งแทนที่ทุกอย่างกับทุกสิ่ง ผลของรหัสของคุณไม่ทำอะไรเลย
— Chad Grant

8
ฉันเดาว่าฉันควรรอที่จะแสดงความคิดเห็นเพื่อให้คนอื่นแก้ไขเสร็จสิ้น ขอโทษ
— Chad Grant

0

การตรวจจับอีเมลในคำตอบของ Travitron ด้านบนไม่ได้ผลสำหรับฉันดังนั้นฉันจึงขยาย / แทนที่ด้วยรหัสต่อไปนี้ (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);

สิ่งนี้ทำให้ที่อยู่อีเมลเช่น " firstname.secondname@one.two.three.co.uk "


รหัสด้านบนจะล้มเหลวในการทดสอบจำนวนมากสำหรับเคสแบบมีขอบ เมื่อตรวจจับ URL จะดีกว่าหากใช้ไลบรารีเฉพาะ นี่คือเหตุผลที่
— Dan Dascalescu

ขอบคุณ @DanDascalescu โดยปกติแล้วมันจะดีกว่าการพูดเกินจริงเสมอ
— Uwe Keim

0

หลังจากป้อนข้อมูลจากหลาย ๆ แหล่งฉันได้วิธีการแก้ปัญหาที่ทำงานได้ดี มันเกี่ยวข้องกับการเขียนรหัสการแทนที่ของคุณเอง

คำตอบ

ซอ

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
รหัสด้านบน (และนิพจน์ทั่วไปโดยทั่วไป) จะล้มเหลวในการทดสอบจำนวนมากสำหรับเคสขนาดเล็ก เมื่อตรวจจับ URL จะดีกว่าหากใช้ไลบรารีเฉพาะ นี่คือเหตุผลที่
— Dan Dascalescu

แดน, มีห้องสมุดแบบนี้เหรอ? แม้ว่าในกรณีนี้เราจะยังคงจับคู่ regex ข้างต้นเพื่อให้รหัสไม่สามารถส่งออกขยะเมื่อขยะบางอย่างเช่น (แม้ว่าห้องสมุดอื่นรับรองขยะเป็น URL / URI ที่ถูกต้อง) เป็นอินพุต
— Mike Mestnik


0

นี่คือทางออกของฉัน:

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

ลองฟังก์ชั่นด้านล่าง:

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

ลองวิธีแก้ปัญหาด้านล่าง

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;
}
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.