Konwertowanie ciągu wejściowego użytkownika na wyrażenie regularne

333

Projektuję tester wyrażeń regularnych w HTML i JavaScript. Użytkownik wprowadzi wyrażenie regularne, ciąg znaków i wybierze funkcję, którą chce przetestować (np. Wyszukaj, dopasuj, zamień itp.) Przyciskiem radiowym, a program wyświetli wyniki, gdy funkcja ta zostanie uruchomiona z podanymi argumentami. Oczywiście będą dodatkowe pola tekstowe do zastąpienia dodatkowych argumentów i tak dalej.

Moim problemem jest pobranie ciągu od użytkownika i przekształcenie go w wyrażenie regularne. Jeśli powiem, że nie muszą mieć //przedrostka, do którego wchodzą, to nie mogą ustawiać flag, takich jak gi i. Muszą więc mieć //wokół wyrażenia, ale jak mogę przekonwertować ten ciąg na wyrażenie regularne? Nie może być literałem, ponieważ jest łańcuchem i nie mogę przekazać go konstruktorowi RegExp, ponieważ nie jest łańcuchem bez //liter. Czy istnieje inny sposób przekształcenia ciągu wejściowego użytkownika w wyrażenie regularne? Czy będę musiał przeanalizować ciąg i flagi wyrażenia regularnego z literami //„s”, a następnie skonstruować go w inny sposób? Czy powinienem je wprowadzić ciąg, a następnie osobno wprowadzić flagi?

Gordon Gustafson
źródło

Odpowiedzi:

611

Użyj konstruktora obiektów RegExp, aby utworzyć wyrażenie regularne z łańcucha:

var re = new RegExp("a|b", "i");
// same as
var re = /a|b/i;
Gumbo
źródło
1
fajnie byłoby mieć narzędzie online z polem wprowadzania danych
holms
61
Robiąc to w ten sposób, musisz uciec przed odwrotnym ukośnikiem, np.var re = new RegExp("\\w+");
JD Smith
12
@holms regex101.com to również świetne narzędzie do wyrażania regularnego
Fran Herrero
2
Zajęło mi trochę czasu, aby zobaczyć, że nie są wymagane końcowe ukośniki
Gerfried,
2
@JDSmith Nie miałem tego na myśli w twoim przykładzie. Miałem na myśli to, że musisz uciec od podwójnych cudzysłowów, jeśli chcesz, aby były częścią wyrażenia regularnego, pod warunkiem, że są na stałe zakodowane. Oczywiście nic z tego nie ma zastosowania, jeśli ciąg znaków znajduje się w zmiennej takiej jak <input>tag HTML. var re = new RegExp("\"\\w+\"");jest przykładem regexu zakodowanego na stałe za pomocą konstruktora RegExp i konieczne jest unikanie podwójnych cudzysłowów . Rozumiem przez ciąg w zmiennej to, że możesz po prostu zrobić var re = new RegExp(str);i strmoże on bez problemu zawierać podwójne cudzysłowy lub ukośniki odwrotne.
Luis Paulo,
66
var flags = inputstring.replace(/.*\/([gimy]*)$/, '$1');
var pattern = inputstring.replace(new RegExp('^/(.*?)/'+flags+'$'), '$1');
var regex = new RegExp(pattern, flags);

lub

var match = inputstring.match(new RegExp('^/(.*?)/([gimy]*)$'));
// sanity check here
var regex = new RegExp(match[1], match[2]);
Anonimowy
źródło
Należy wziąć pod uwagę, że rozpoznano niepoprawne dane wejściowe /\/.
Gumbo
8
Lub pozwól konstruktorowi RegExp zawieść, „kończąc \ w wyrażeniu regularnym”, zamiast pisać skomplikowanego analizatora składni.
Anonimowy
21

Oto jedna linijka: str.replace(/[|\\{}()[\]^$+*?.]/g, '\\$&')

Mam go z modułu NPM „ escape-string-regexp” .

Wypróbowanie:

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

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

Używanie otagowanych literałów szablonów z obsługą flag:

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
Rivenfall
źródło
15

Użyj konstruktora obiektów JavaScript RegExp .

var re = new RegExp("\\w+");
re.test("hello");

Możesz przekazać flagi jako drugi argument ciągu do konstruktora. Szczegółowe informacje można znaleźć w dokumentacji.

Ayman Hourieh
źródło
9

W moim przypadku dane wprowadzane przez użytkownika były czasem otoczone ogranicznikami, a czasem nie. dlatego dodałem inny przypadek ..

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);
}
staabm
źródło
3
zawsze możesz użyć .split()funkcji zamiast długiego łańcucha wyrażeń regularnych. regParts = inputstring.split('/')spowoduje to utworzenie regParts[1]łańcucha wyrażeń regularnych i regParts[2]ograniczników (przy założeniu, że ustawienie wyrażenia regularnego jest /.../gim). Możesz sprawdzić, czy istnieją separatory z regParts[2].length < 0.
Jaketr00 21.04.16
3

Sugeruję również dodanie osobnych pól wyboru lub pola tekstowego dla specjalnych flag. W ten sposób jasne jest, że użytkownik nie musi dodawać żadnych //. W przypadku zamiany podaj dwa pola tekstowe. Ułatwi ci to życie.

Dlaczego? Ponieważ w przeciwnym razie niektórzy użytkownicy dodadzą //, a inni nie. A niektóre popełniają błąd składniowy. Następnie, po usunięciu //'s, możesz skończyć z poprawnym składniowo wyrażeniem regularnym, który nie jest podobny do zamierzonego przez użytkownika, co prowadzi do dziwnych zachowań (z perspektywy użytkownika).

Stephan202
źródło
2

Działa to również wtedy, gdy ciąg jest nieprawidłowy lub nie zawiera flag itp .:

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

Kofifus
źródło
2

Jeśli naprawdę chcesz przekonwertować ciąg na wyrażenie regularne, spróbuj użyć następującej funkcji:

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

Możesz go używać w następujący sposób:

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

Dla odniesienia, oto sformatowana i bardziej nowoczesna wersja:

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)
}
Richie Bendall
źródło
1

Dzięki wcześniejszym odpowiedziom bloki te służą również jako rozwiązanie ogólnego zastosowania do stosowania ciągów konfigurowalnych w RegEx .. do filtrowania tekstu:

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, '');
Gene Bo
źródło
1

Możesz poprosić o flagi za pomocą pól wyboru, a następnie zrobić coś takiego:

var userInput = formInput;
var flags = '';
if(formGlobalCheckboxChecked) flags += 'g';
if(formCaseICheckboxChecked) flags += 'i';
var reg = new RegExp(userInput, flags);
Pim Jager
źródło
wygląda na to, że RegEx brakuje końcowego p . Stos nie pozwala mi na edycję 1 postaci
Gene Bo
-3

Używam evalaby rozwiązać ten problem.

Na przykład:

    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()));
    }
Playhi
źródło
3
eval na userInput to szalone ryzyko bezpieczeństwa
Samuel Faure
1
Pan Bobby Stoły!
Luiz Felipe