จะแทนที่ 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.