มีฟังก์ชั่น RegExp.escape ใน Javascript หรือไม่


442

ฉันแค่ต้องการสร้างการแสดงออกปกติจากสตริงที่เป็นไปได้ใด ๆ

var usersString = "Hello?!*`~World()[]";
var expression = new RegExp(RegExp.escape(usersString))
var matches = "Hello".match(expression);

มีวิธีการในตัวสำหรับที่? ถ้าไม่ใช่คนใช้อะไร RegExp.escapeทับทิมมี ฉันไม่รู้สึกว่าฉันจะต้องเขียนของตัวเองมันต้องมีอะไรบางอย่างที่เป็นมาตรฐาน ขอบคุณ!


15
เพียงแค่ต้องการที่จะอัปเดตชาวบ้านที่คุณใช้RegExp.escapeงานอยู่และใครก็ตามที่คิดว่าพวกเขามีความคิดเห็นที่มีค่ายินดีต้อนรับที่จะมีส่วนร่วม core-js และ polyfills อื่น ๆ นำเสนอ
Benjamin Gruenbaum

5
ตามการอัปเดตล่าสุดของคำตอบนี้ข้อเสนอนี้ถูกปฏิเสธ: ดูปัญหา
ลองจับได้ในที่สุด

คำตอบ:


573

ฟังก์ชั่นที่เชื่อมโยงด้านบนไม่เพียงพอ มันไม่สามารถหลบหนี^หรือ$(เริ่มต้นและสิ้นสุดของสตริง) หรือ-ซึ่งในกลุ่มตัวละครจะใช้สำหรับช่วง

ใช้ฟังก์ชั่นนี้:

function escapeRegex(string) {
    return string.replace(/[-\/\\^$*+?.()|[\]{}]/g, '\\$&');
}

ในขณะที่มันอาจดูเหมือนไม่จำเป็นตอนแรกการหลบหนี-(และ^) ทำให้ฟังก์ชั่นที่เหมาะสมสำหรับการหลบหนีตัวละครจะถูกแทรกลงในคลาสตัวละครรวมทั้งเนื้อหาของ regex

การหลบหนี/ทำให้ฟังก์ชั่นเหมาะสำหรับการหลบหนีตัวละครที่จะใช้ในตัวอักษร JS regex สำหรับการ eval ในภายหลัง

เนื่องจากไม่มีข้อเสียใด ๆ ในการหลีกเลี่ยงทั้งสองข้อจึงควรหลีกเลี่ยงที่จะครอบคลุมกรณีการใช้งานที่กว้างขึ้น

และใช่มันเป็นความล้มเหลวที่น่าผิดหวังที่นี่ไม่ใช่ส่วนหนึ่งของ JavaScript มาตรฐาน


16
ที่จริงแล้วเราไม่จำเป็นต้องหลบหนี/เลย
หนาม

28
@Paul: Perl quotemeta( \Q), Python re.escape, PHP preg_quote, Ruby Regexp.quote...
bobince

13
หากคุณกำลังจะใช้ฟังก์ชั่นนี้ในการวนซ้ำมันอาจเป็นการดีที่สุดที่จะทำให้อ็อบเจกต์ RegExp เป็นตัวแปรของตัวเองvar e = /[\-\[\]\/\{\}\(\)\*\+\?\.\\\^\$\|]/g;และจากนั้นฟังก์ชั่นของคุณก็คือreturn s.replace(e, '\\$&');วิธีนี้
สไตล์

15
ข้อโต้แย้งมาตรฐานต่อการเพิ่มวัตถุในตัวใช้งานได้ที่นี่ไม่? จะเกิดอะไรขึ้นถ้า ECMAScript เวอร์ชันอนาคตRegExp.escapeมีการใช้งานที่แตกต่างจากของคุณ? จะดีกว่าไหมถ้าฟังก์ชั่นนี้จะไม่ติดกับอะไรเลย?
Mark Amery

15
bobince ไม่สนใจความคิดเห็นของ eslint
bobince

114

สำหรับทุกคนที่ใช้ lodash เนื่องจาก v3.0.0มีฟังก์ชั่น_.escapeRegExpอยู่ในตัว:

_.escapeRegExp('[lodash](https://lodash.com/)');
// → '\[lodash\]\(https:\/\/lodash\.com\/\)'

และในกรณีที่คุณไม่ต้องการใช้ไลบรารี่เต็มคุณอาจจำเป็นต้องใช้ฟังก์ชั่นนั้น !


6
มีแม้กระทั่งแพคเกจ npm ของเพียงแค่นี้! npmjs.com/package/lodash.escaperegexp
Ted Pennings

1
นี่นำเข้าโหลดโค้ดที่ไม่จำเป็นต้องมีสำหรับสิ่งง่าย ๆ ใช้คำตอบของ bobince ... เหมาะสำหรับฉันและมันมีจำนวนไบต์ที่น้อยกว่าในการโหลดมากกว่ารุ่น Lodash!
Rob Evans

6
@RobEvans คำตอบของฉันเริ่มต้นด้วย"สำหรับทุกคนที่ใช้ lodash"และผมได้กล่าวถึงการที่คุณสามารถกำหนดให้เท่านั้นescapeRegExpฟังก์ชั่น
gustavohenke

2
@ gustavohenke ขออภัยฉันควรจะชัดเจนมากขึ้นเล็กน้อยฉันรวมโมดูลที่เชื่อมโยงกับใน "ฟังก์ชั่นนั้น" และนั่นคือสิ่งที่ฉันแสดงความคิดเห็น ถ้าคุณดูมันเป็นรหัสที่ค่อนข้างมากสำหรับสิ่งที่ควรจะมีประสิทธิภาพฟังก์ชั่นเดียวกับ regexp เดียวในนั้น เห็นด้วยถ้าคุณกำลังใช้ lodash อยู่แล้วมันก็สมเหตุสมผลที่จะใช้ แต่ไม่เช่นนั้นให้ใช้คำตอบอื่น ขออภัยสำหรับความคิดเห็นที่ไม่ชัดเจน
Rob Evans

2
@maddob ฉันไม่เห็นว่า \ x3 คุณพูดถึง: สตริงที่ถูกหลบหนีของฉันดูดีเพียงแค่สิ่งที่ฉันคาดหวัง
Federico Fissore

43

นิพจน์ส่วนใหญ่ที่นี่แก้ปัญหากรณีใช้งานที่เฉพาะเจาะจง

ไม่เป็นไร แต่ฉันชอบวิธีการ "ทำงานได้ตลอดเวลา"

function regExpEscape(literal_string) {
    return literal_string.replace(/[-[\]{}()*+!<=:?.\/\\^$|#\s,]/g, '\\$&');
}

นี่จะ "หลบหนีอย่างเต็มที่" สตริงตัวอักษรสำหรับการใช้งานใด ๆ ต่อไปนี้ในนิพจน์ทั่วไป

  • การแทรกในนิพจน์ปกติ เช่นnew RegExp(regExpEscape(str))
  • การแทรกในคลาสอักขระ เช่นnew RegExp('[' + regExpEscape(str) + ']')
  • การแทรกตัวระบุจำนวนเต็ม เช่นnew RegExp('x{1,' + regExpEscape(str) + '}')
  • การดำเนินการในเอ็นจินนิพจน์ทั่วไปที่ไม่ใช่ JavaScript

ตัวละครพิเศษที่ครอบคลุม:

  • -: สร้างช่วงอักขระในคลาสอักขระ
  • [/ ]: เริ่ม / จบคลาสอักขระ
  • {/ }: เริ่ม / สิ้นสุดตัวระบุการคำนวณ
  • (/ ): เริ่ม / สิ้นสุดกลุ่ม
  • */ +/ ?: ระบุประเภทการทำซ้ำ
  • .: ตรงกับตัวละครใด ๆ
  • \: หนีอักขระและเริ่มเอนทิตี้
  • ^: ระบุจุดเริ่มต้นของการจับคู่โซนและคัดค้านการจับคู่ในคลาสอักขระ
  • $: ระบุจุดสิ้นสุดของโซนที่ตรงกัน
  • |: ระบุการสลับ
  • #: ระบุความคิดเห็นในโหมดระยะห่างฟรี
  • \s: ข้ามไปในโหมดระยะห่างฟรี
  • ,: คั่นค่าในตัวระบุการคำนวณ
  • /: เริ่มหรือสิ้นสุดการแสดงออก
  • :: เสร็จสิ้นประเภทกลุ่มพิเศษและเป็นส่วนหนึ่งของคลาสอักขระสไตล์ Perl
  • !: ลบกลุ่มที่มีความกว้างเป็นศูนย์
  • </ =: ส่วนหนึ่งของข้อมูลจำเพาะกลุ่มความกว้างเป็นศูนย์

หมายเหตุ:

  • /ไม่จำเป็นอย่างเคร่งครัดในรสชาติของการแสดงออกปกติใด ๆ แต่ก็ปกป้องในกรณีที่มีคน(สั่น)eval("/" + pattern + "/");ไม่
  • , ตรวจสอบให้แน่ใจว่าถ้าสตริงนั้นมีความหมายว่าเป็นจำนวนเต็มในตัวระบุตัวเลขมันจะทำให้เกิดข้อผิดพลาดในการรวบรวม RegExp อย่างถูกต้องแทนที่จะรวบรวมผิดอย่างเงียบ ๆ
  • #และ\sไม่จำเป็นต้องหลบหนีใน JavaScript แต่ทำในรสชาติอื่น ๆ อีกมากมาย พวกเขาจะหลบหนีที่นี่ในกรณีที่การแสดงออกปกติจะถูกส่งต่อไปยังโปรแกรมอื่น

หากคุณต้องการหลักฐานในอนาคตเกี่ยวกับการแสดงออกปกติต่อการเพิ่มความสามารถของเอนจิน JavaScript regex ฉันขอแนะนำให้ใช้ paranoid เพิ่มเติม:

function regExpEscapeFuture(literal_string) {
    return literal_string.replace(/[^A-Za-z0-9_]/g, '\\$&');
}

ฟังก์ชันนี้จะยกเว้นอักขระทั้งหมดยกเว้นที่รับประกันอย่างชัดเจนว่าจะไม่ใช้สำหรับไวยากรณ์ในรสชาติการแสดงออกปกติในอนาคต


สำหรับผู้ที่รักการสุขาภิบาลอย่างแท้จริงให้พิจารณากรณีขอบนี้:

var s = '';
new RegExp('(choice1|choice2|' + regExpEscape(s) + ')');

สิ่งนี้ควรรวบรวมใน JavaScript แต่จะไม่อยู่ในรสชาติอื่น ๆ หากตั้งใจที่จะส่งผ่านไปยังรสชาติอื่นs === ''ควรตรวจสอบกรณีที่ว่างด้วยตนเองเช่น:

var s = '';
new RegExp('(choice1|choice2' + (s ? '|' + regExpEscape(s) : '') + ')');

1
/ไม่จำเป็นต้องได้รับการหลบหนีใน[...]ชั้นเรียนตัวอักษร
Dan Dascalescu

1
ส่วนใหญ่ไม่จำเป็นต้องหลบหนี "สร้างช่วงอักขระในคลาสอักขระ" - คุณจะไม่เคยอยู่ในคลาสอักขระภายในสตริง "ระบุความคิดเห็นในโหมดเว้นวรรคว่าง, ข้ามไปในโหมดว่างระยะห่าง" - ไม่รองรับในจาวาสคริปต์ "แยกค่าในตัวระบุการคำนวณ" - คุณไม่เคยอยู่ในตัวระบุตัวเลขภายในสตริง นอกจากนี้คุณไม่สามารถเขียนข้อความใด ๆ ภายในข้อกำหนดคุณสมบัติ "เริ่มหรือจบการแสดงออก" - ไม่จำเป็นต้องหลบหนี Eval ไม่ได้เป็นอย่างที่มันต้องการมากขึ้น [จะดำเนินการต่อในความคิดเห็นถัดไป]
Qwertiy

"เสร็จสิ้นประเภทกลุ่มพิเศษและส่วนหนึ่งของคลาสอักขระสไตล์ Perl" - ดูเหมือนจะไม่สามารถใช้ได้ใน javascript "ลบกลุ่มที่มีความกว้างเป็นศูนย์ส่วนหนึ่งของข้อมูลจำเพาะของกลุ่มที่มีความกว้างเป็นศูนย์" - คุณไม่มีกลุ่มที่อยู่ภายในสตริง
Qwertiy

@Qwertiy เหตุผลของการหนีพิเศษเหล่านี้คือการกำจัดเคสขอบซึ่งอาจทำให้เกิดปัญหาในบางกรณีการใช้งาน ตัวอย่างเช่นผู้ใช้ฟังก์ชั่นนี้อาจต้องการที่จะแทรกสตริง regex ที่หลบหนีลงใน regex อื่นเป็นส่วนหนึ่งของกลุ่มหรือแม้กระทั่งการใช้ในภาษาอื่นนอกเหนือจาก Javascript ฟังก์ชั่นไม่ได้ทำให้สมมติฐานเช่น "ฉันจะไม่เป็นส่วนหนึ่งของตัวละครคลาส" เพราะมันหมายถึงการเป็นทั่วไป สำหรับแนวทาง YAGNI เพิ่มเติมดูคำตอบอื่น ๆ ที่นี่
Pi Marillion

ดีมาก. ทำไม _ ถึงไม่หนีออกมา อะไรทำให้มั่นใจได้ว่ามันจะไม่กลายเป็นไวยากรณ์ของ regex ในภายหลัง
madprops

30

คำแนะนำของ Mozilla Developer Network สำหรับ Regular Expressionsมีฟังก์ชั่นหลบหนีนี้:

function escapeRegExp(string) {
  return string.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'); // $& means the whole matched string
}

@DanDascalescu คุณพูดถูก หน้า MDN ได้รับการอัปเดตและ=ไม่รวมอยู่ในนั้นอีกต่อไป
quietmint

21

ในวิดเจ็ตการเติมข้อความอัตโนมัติของ jQueryUI (เวอร์ชั่น 1.9.1) พวกเขาใช้ regex ที่แตกต่างกันเล็กน้อย (Line 6753) นี่คือนิพจน์ทั่วไปที่รวมเข้ากับ @bobince

RegExp.escape = function( value ) {
     return value.replace(/[\-\[\]{}()*+?.,\\\^$|#\s]/g, "\\$&");
}

4
ข้อแตกต่างอย่างเดียวคือพวกมันหลบหนี,(ซึ่งไม่ใช่เมตาเบอเรเตอร์) และ#และช่องว่างที่สำคัญในโหมดระยะห่างฟรีเท่านั้น (ซึ่งไม่ได้รับการสนับสนุนโดย JavaScript) อย่างไรก็ตามพวกเขาทำให้ถูกต้องไม่ได้ที่จะหลบหนีไปข้างหน้าเฉือน
Martin Ender

18
หากคุณต้องการที่จะนำมาใช้ดำเนินงาน jQuery UI $.ui.autocomplete.escapeRegex(myString)มากกว่าวางโค้ดในประเทศไปกับ
Scott Stafford

2
lodash มีสิ่งนี้ด้วยเช่นกัน _. escapeRegExp และnpmjs.com/package/lodash.escaperegexp
Ted Pennings

v1.12 เหมือนกันโอเค!
Peter Krauss

13

ไม่มีอะไรควรป้องกันคุณจากการหลบหนีอักขระที่ไม่ใช่ตัวอักษรและตัวเลขทุกตัว:

usersString.replace(/(?=\W)/g, '\\');

คุณสูญเสียความสามารถในการอ่านระดับหนึ่งเมื่อทำre.toString()แต่คุณจะได้รับความเรียบง่าย (และความปลอดภัย)

ตาม ECMA-262 บนมือข้างหนึ่งปกติสำนวนที่ว่า "ตัวละครไวยากรณ์" อยู่เสมอไม่ใช่ตัวเลขดังกล่าวว่าผลที่ได้คือการรักษาความปลอดภัยและลำดับหนีพิเศษ ( \d, \w, \n) อยู่เสมอและตัวเลขดังกล่าวว่าไม่มีการหลบหนีการควบคุมที่เป็นเท็จจะมีการผลิต .


ง่ายและมีประสิทธิภาพ ฉันชอบสิ่งนี้ดีกว่าคำตอบที่ยอมรับ สำหรับเบราว์เซอร์เก่า (จริงๆ) .replace(/[^\w]/g, '\\$&')จะทำงานในลักษณะเดียวกัน
Tomas Langkaas

6
สิ่งนี้ล้มเหลวในโหมด Unicode ตัวอย่างเช่นส่งnew RegExp('🍎'.replace(/(?=\W)/g, '\\'), 'u')ข้อยกเว้นเนื่องจาก\Wจับคู่รหัสแต่ละหน่วยของคู่ตัวแทนแทนแยกกันทำให้เกิดรหัสยกเว้นที่ไม่ถูกต้อง
Alexey Lebedev

1
ทางเลือก:.replace(/\W/g, "\\$&");
Miguel Pynto

@AlexeyLebedev คำตอบของคุณได้รับการแก้ไขแล้วเพื่อจัดการกับโหมด Unicode หรือไม่ หรือมีวิธีแก้ปัญหาอื่นที่ทำในขณะที่รักษาความเรียบง่ายนี้
johny ทำไม

11

มีข้อเสนอ ES7 สำหรับ RegExp.escape ที่ https://github.com/benjamingr/RexExp.escape/กับ polyfill ใช้ได้ที่https://github.com/ljharb/regexp.escape


9
ลักษณะเช่นนี้ไม่ได้ทำให้มันกลายเป็น ES7 นอกจากนี้ยังดูเหมือนว่ามันถูกปฏิเสธในความโปรดปรานของการมองหาแท็กแม่แบบ
จอห์น

6

นี่เป็นรุ่นที่สั้นกว่า

RegExp.escape = function(s) {
    return s.replace(/[$-\/?[-^{|}]/g, '\\$&');
}

ซึ่งรวมถึงตัวละครที่ไม่ใช่เมตาของ%, &, 'และ,แต่สเปค JavaScript ช่วยให้ RegExp นี้


2
ฉันจะไม่ใช้เวอร์ชั่นที่สั้นกว่านี้เนื่องจากช่วงตัวละครจะซ่อนรายการของตัวละครซึ่งทำให้ยากต่อการตรวจสอบความถูกต้องตั้งแต่แรกเห็น
nhahtdh

@nhahtdh ฉันอาจจะไม่ได้อย่างใดอย่างหนึ่ง แต่มันถูกโพสต์ที่นี่สำหรับข้อมูล
kzh

@ kzh: การโพสต์ "สำหรับข้อมูล" ช่วยให้น้อยกว่าการโพสต์เพื่อความเข้าใจ คุณไม่เห็นด้วยหรือไม่ว่าคำตอบของฉันชัดเจนขึ้น?
Dan Dascalescu

อย่างน้อย.ก็พลาดไป และ(). หรือไม่? [-^แปลก ฉันจำไม่ได้ว่ามีอะไร
Qwertiy

เหล่านั้นอยู่ในช่วงที่ระบุ
kzh


3

แทนที่จะหนีจากตัวละครที่จะทำให้เกิดปัญหาในการแสดงออกปกติของคุณ (เช่นบัญชีดำ) ทำไมไม่ลองใช้บัญชีขาวแทน วิธีนี้จะถือว่าตัวละครแต่ละตัวเสีย

สำหรับตัวอย่างนี้ให้ถือว่านิพจน์ต่อไปนี้:

RegExp.escape('be || ! be');

รายการตัวอักษรที่อนุญาตนี้จำนวนและช่องว่าง:

RegExp.escape = function (string) {
    return string.replace(/([^\w\d\s])/gi, '\\$1');
}

ผลตอบแทน:

"be \|\| \! be"

สิ่งนี้อาจหลบหนีตัวละครที่ไม่จำเป็นต้องหลบหนี แต่สิ่งนี้ไม่ได้ขัดขวางการแสดงออกของคุณ (อาจมีบทลงโทษเล็กน้อยบางครั้ง - แต่มันก็คุ้มค่าสำหรับความปลอดภัย)


สิ่งนี้แตกต่างจากคำตอบของ @ filip หรือไม่ stackoverflow.com/a/40562456/209942
johny ทำไม


1

ฟังก์ชั่นในคำตอบอื่น ๆ นั้นมากไปสำหรับการหลบหนีการแสดงออกปกติทั้งหมด (พวกเขาอาจมีประโยชน์สำหรับการหลบหนีบางส่วนของการแสดงออกปกติที่จะถูกนำมาต่อกันเป็น regexps ที่ใหญ่กว่า)

ถ้าคุณหนี regexp ทั้งหมดและจะทำกับมัน quoting metacharacters ที่มีทั้งแบบสแตนด์อโลน ( ., ?, +, *, ^, $, |, \) หรือเริ่มต้นสิ่งที่ ( (, [, {) เป็นสิ่งที่คุณจำเป็นต้องใช้:

String.prototype.regexEscape = function regexEscape() {
  return this.replace(/[.?+*^$|({[\\]/g, '\\$&');
};

และใช่มันน่าผิดหวังที่ JavaScript ไม่มีฟังก์ชั่นในตัวนี้


สมมติว่าคุณหนีเข้าของผู้ใช้(text)nextและใส่ไว้ใน: (?:+ )+ วิธีการของคุณจะให้สตริงผลลัพธ์(?:\(text)next)ที่ล้มเหลวในการรวบรวม โปรดทราบว่านี้ค่อนข้างแทรกที่เหมาะสมไม่ได้บางคนบ้าเหมือนre\การป้อนข้อมูล + re(ในกรณีนี้โปรแกรมเมอร์สามารถตำหนิสำหรับการทำสิ่งที่โง่)
nhahtdh

1
@nhahtdh: คำตอบของฉันกล่าวถึงโดยเฉพาะการหลบหนีการแสดงออกปกติและ "กำลังทำ" กับพวกเขาไม่ใช่ส่วน (หรือส่วนในอนาคต) ของ regexps กรุณาเลิกทำการ downvote หรือไม่
Dan Dascalescu

มันเป็นกรณีที่คุณจะหลีกเลี่ยงการแสดงออกทั้งหมด - มีการดำเนินงานของสตริงซึ่งเร็วกว่ามากเมื่อเทียบกับ regex ถ้าคุณต้องการที่จะทำงานกับสตริงตัวอักษร
nhahtdh

นี่ไม่ได้พูดถึงว่ามันไม่ถูกต้อง - \ควรหลีกเลี่ยงเนื่องจาก regex ของคุณจะ\wไม่เสียหาย นอกจากนี้ JavaScript ดูเหมือนจะไม่อนุญาตการติดตาม)อย่างน้อยนั่นก็เป็นสิ่งที่ Firefox ทำผิดพลาด
nhahtdh

1
โปรดระบุส่วนที่เกี่ยวกับการปิด)
nhahtdh

1

อีกวิธีหนึ่งที่ปลอดภัยกว่าคือการหลบหนีตัวละครทุกตัว (ไม่ใช่แค่ตัวละครพิเศษที่เรารู้จัก) โดยใช้รูปแบบการหลบหนีแบบ Unicode \u{code}:

function escapeRegExp(text) {
    return Array.from(text)
           .map(char => `\\u{${char.charCodeAt(0).toString(16)}}`)
           .join('');
}

console.log(escapeRegExp('a.b')); // '\u{61}\u{2e}\u{62}'

โปรดทราบว่าคุณต้องผ่านการuตั้งค่าสถานะเพื่อให้วิธีนี้ทำงาน:

var expression = new RegExp(escapeRegExp(usersString), 'u');

1

มีเพียงเคยและเคยจะเป็นตัวละครเมตา 12 ที่จะต้องมีการหลบหนี
เพื่อพิจารณาตัวอักษร

ไม่สำคัญว่าจะทำอะไรกับสตริง escaped แทรกลงใน
wrapper regex ที่สมดุลผนวกท้ายไม่สำคัญ

ทำสตริงแทนที่โดยใช้สิ่งนี้

var escaped_string = oldstring.replace( /[\\^$.|?*+()[{]/g, '\\$&' );

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