การแปลงสตริงอินพุตผู้ใช้เป็นนิพจน์ทั่วไป


333

ฉันกำลังออกแบบเครื่องมือทดสอบนิพจน์ปกติใน HTML และ JavaScript ผู้ใช้จะป้อน regex สตริงและเลือกฟังก์ชั่นที่พวกเขาต้องการทดสอบด้วย (เช่นค้นหาจับคู่แทนที่ ฯลฯ ) ผ่านปุ่มตัวเลือกและโปรแกรมจะแสดงผลลัพธ์เมื่อฟังก์ชันนั้นทำงานด้วยอาร์กิวเมนต์ที่ระบุ โดยธรรมชาติจะมีกล่องข้อความเพิ่มเติมสำหรับการขัดแย้งพิเศษเพื่อแทนที่และเช่น

ปัญหาของฉันคือรับสตริงจากผู้ใช้และเปลี่ยนเป็นนิพจน์ทั่วไป ถ้าผมบอกว่าพวกเขาไม่จำเป็นต้องมี//'s รอบ regex ที่พวกเขาใส่ธงแล้วพวกเขาก็ไม่สามารถตั้งค่าเช่นและg iดังนั้นพวกเขาจะต้องมีการ//แสดงออก แต่ฉันจะแปลงสตริงนั้นเป็น regex ได้อย่างไร มันไม่สามารถเป็นตัวอักษรตั้งแต่สตริงและฉันไม่สามารถผ่านไปยังตัวสร้างนิพจน์ทั่วไปตั้งแต่สตริงไม่มันโดยไม่ต้อง//'s มีวิธีอื่นที่จะทำให้ผู้ใช้ป้อนสตริงใน regex หรือไม่ ฉันจะต้องแยกสตริงและธงของ regex ด้วย//แล้วสร้างมันอีกวิธีหนึ่งหรือไม่ ฉันควรให้พวกเขาใส่สายแล้วใส่ธงแยกต่างหาก?

คำตอบ:


611

ใช้ตัวสร้างวัตถุ RegExpเพื่อสร้างนิพจน์ปกติจากสตริง:

var re = new RegExp("a|b", "i");
// same as
var re = /a|b/i;

1
จะดีจะมีเครื่องมือออนไลน์ที่มีข้อมูลป้อน
Holms

61
เมื่อทำเช่นนี้คุณจะต้องหลีกเลี่ยงแบ็กสแลชเช่นvar re = new RegExp("\\w+");
JD Smith

12
@holms regex101.comเป็นเครื่องมือออนไลน์ regex ที่ยอดเยี่ยมเช่นกัน
Fran Herrero

2
ฉันใช้เวลาสักพักกว่าจะเห็นว่าไม่จำเป็นต้องใช้เครื่องหมายทับเพื่อต่อท้าย
Gerfried

2
@JDS ฉันไม่ได้ตั้งใจในตัวอย่างของคุณ ฉันหมายความว่าคุณต้องหลีกเลี่ยงอัญประกาศถ้าคุณต้องการให้มันเป็นส่วนหนึ่งของ regex หากมันเป็นรหัสยาก แน่นอนไม่มีสิ่งนี้ใช้ถ้าสตริงอยู่ในตัวแปรเช่นจาก<input>แท็ก HTML var re = new RegExp("\"\\w+\"");เป็นตัวอย่างของ regex ฮาร์ดโค้ดที่ใช้ตัวสร้าง RegExp และการหลีกเลี่ยงการอัญประกาศเป็นสิ่งจำเป็น สิ่งที่ฉันหมายถึงโดยสตริงในตัวแปรคือคุณสามารถทำได้var re = new RegExp(str);และstrอาจมีเครื่องหมายคำพูดหรือแบ็กสแลชคู่โดยไม่มีปัญหา
ลูอิสเปาโล

66
var flags = inputstring.replace(/.*\/([gimy]*)$/, '$1');
var pattern = inputstring.replace(new RegExp('^/(.*?)/'+flags+'$'), '$1');
var regex = new RegExp(pattern, flags);

หรือ

var match = inputstring.match(new RegExp('^/(.*?)/([gimy]*)$'));
// sanity check here
var regex = new RegExp(match[1], match[2]);

คุณควรพิจารณาว่ามีการ/\/จดจำอินพุตที่ไม่ถูกต้องเช่นนั้น
Gumbo

8
หรือปล่อยให้ตัวสร้าง RegExp ล้มเหลว "ต่อท้าย \ ในนิพจน์ทั่วไป" แทนที่จะเขียนโปรแกรมแยกวิเคราะห์ที่ซับซ้อน
ไม่ระบุชื่อ

21

นี่คือหนึ่งซับ: str.replace(/[|\\{}()[\]^$+*?.]/g, '\\$&')

ฉันได้รับจากโมดูล NPM escape-string-regexp

ลองดูสิ:

escapeStringRegExp.matchOperatorsRe = /[|\\{}()[\]^$+*?.]/g;
function escapeStringRegExp(str) {
    return str.replace(escapeStringRegExp.matchOperatorsRe, '\\$&');
}

console.log(new RegExp(escapeStringRegExp('example.com')));
// => /example\.com/

การใช้ตัวอักษรเทมเพลตที่มีแท็กพร้อมการสนับสนุนค่าสถานะ:

function str2reg(flags = 'u') {
    return (...args) => new RegExp(escapeStringRegExp(evalTemplate(...args))
        , flags)
}

function evalTemplate(strings, ...values) {
    let i = 0
    return strings.reduce((str, string) => `${str}${string}${
        i < values.length ? values[i++] : ''}`, '')
}

console.log(str2reg()`example.com`)
// => /example\.com/u


9

ในกรณีของฉันบางครั้งข้อมูลผู้ใช้ถูกคั่นด้วยตัวคั่นและบางครั้งก็ไม่ ดังนั้นฉันจึงเพิ่มอีกกรณี ..

var regParts = inputstring.match(/^\/(.*?)\/([gim]*)$/);
if (regParts) {
    // the parsed pattern had delimiters and modifiers. handle them. 
    var regexp = new RegExp(regParts[1], regParts[2]);
} else {
    // we got pattern string without delimiters
    var regexp = new RegExp(inputstring);
}

3
คุณสามารถใช้.split()ฟังก์ชันแทนสตริง regex ที่ยาวได้เสมอ regParts = inputstring.split('/')สิ่งนี้จะทำให้regParts[1]สตริง regex และregParts[2]ตัวคั่น (สมมติว่าการตั้งค่าของ regex คือ/.../gim) คุณสามารถตรวจสอบว่ามีตัวคั่นด้วยregParts[2].length < 0หรือไม่
Jaketr00

3

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

ทำไม? เพราะมิฉะนั้นผู้ใช้บางคนจะเพิ่ม//ในขณะที่คนอื่นจะไม่ และบางคนจะทำให้เกิดข้อผิดพลาดทางไวยากรณ์ จากนั้นหลังจากที่คุณแยกส่วน//คุณอาจท้ายด้วย regex ที่ถูกต้อง syntactically ที่ไม่เหมือนสิ่งที่ผู้ใช้ตั้งใจนำไปสู่พฤติกรรมแปลก ๆ (จากมุมมองของผู้ใช้)


2

สิ่งนี้จะทำงานได้เช่นกันเมื่อสตริงไม่ถูกต้องหรือไม่มีธง ฯลฯ :

function regExpFromString(q) {
  let flags = q.replace(/.*\/([gimuy]*)$/, '$1');
  if (flags === q) flags = '';
  let pattern = (flags ? q.replace(new RegExp('^/(.*?)/' + flags + '$'), '$1') : q);
  try { return new RegExp(pattern, flags); } catch (e) { return null; }
}

console.log(regExpFromString('\\bword\\b'));
console.log(regExpFromString('\/\\bword\\b\/gi'));
            


2

หากคุณจริงๆต้องการแปลงสตริงเพื่อ regex ให้ลองใช้ฟังก์ชั่นต่อไปนี้:

function String2Regex(s){return new RegExp(s.match(/\/(.+)\/.*/)[1], s.match(/\/.+\/(.*)/)[1]);}

คุณสามารถใช้มันได้เช่น:

"abc".match(String2Regex("/a/g"))
> ["a"]

สำหรับการอ้างอิงนี่คือรูปแบบที่จัดรูปแบบและทันสมัยกว่า:

const String2Regex = str => {
  // Main regex
  const main = str.match(/\/(.+)\/.*/)[1]

  // Regex options
  const options = str.match(/\/.+\/(.*)/)[1]

  // Return compiled regex
  return new RegExp(main, options)
}

1

ต้องขอบคุณคำตอบก่อนหน้านี้บล็อกนี้ทำหน้าที่เป็นโซลูชันทั่วไปสำหรับการใช้สตริงที่กำหนดค่าได้ใน RegEx .. สำหรับการกรองข้อความ:

var permittedChars = '^a-z0-9 _,.?!@+<>';
permittedChars = '[' + permittedChars + ']';

var flags = 'gi';
var strFilterRegEx = new RegExp(permittedChars, flags);

log.debug ('strFilterRegEx: ' + strFilterRegEx);

strVal = strVal.replace(strFilterRegEx, '');
// this replaces hard code solt:
// strVal = strVal.replace(/[^a-z0-9 _,.?!@+]/ig, '');

1

คุณสามารถขอธงโดยใช้ช่องทำเครื่องหมายจากนั้นทำสิ่งนี้:

var userInput = formInput;
var flags = '';
if(formGlobalCheckboxChecked) flags += 'g';
if(formCaseICheckboxChecked) flags += 'i';
var reg = new RegExp(userInput, flags);

ดูเหมือนว่าRegExจะหายไปต่อท้ายp .. สแต็คไม่ยอมให้ฉันแก้ไขตัวละคร 1 ตัว
Gene Bo

-3

ฉันใช้evalเพื่อแก้ปัญหานี้

ตัวอย่างเช่น:

    function regex_exec() {

        // Important! Like @Samuel Faure mentioned, Eval on user input is a crazy security risk, so before use this method, please take care of the security risk. 
        var regex = $("#regex").val();

        // eval()
        var patt = eval(userInput);

        $("#result").val(patt.exec($("#textContent").val()));
    }

3
การประเมินจาก userInput เป็นความเสี่ยงด้านความปลอดภัย
Samuel Faure

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