Convertendo String de Entrada do Usuário em Expressão Regular

333

Estou projetando um testador de expressões regulares em HTML e JavaScript. O usuário digitará um regex, uma string e escolherá a função com a qual deseja testar (por exemplo, pesquisar, corresponder, substituir etc.) via botão de opção e o programa exibirá os resultados quando essa função for executada com os argumentos especificados. Naturalmente, haverá caixas de texto extras para substituir os argumentos extras.

Meu problema é obter a string do usuário e transformá-la em uma expressão regular. Se eu disser que eles não precisam ter //o regex que inserem, eles não podem definir sinalizadores, como ge i. Portanto, eles precisam ter os caracteres //ao redor da expressão, mas como posso converter essa string em um regex? Não pode ser um literal, pois é uma string, e não posso transmiti-lo ao construtor RegExp, pois não é uma string sem os //. Existe alguma outra maneira de transformar uma sequência de entrada do usuário em uma regex? Terei que analisar a string e as bandeiras do regex com os //e depois construí-lo de outra maneira? Devo fazê-los inserir uma sequência e inserir as bandeiras separadamente?

Gordon Gustafson
fonte

Respostas:

611

Use o construtor de objeto RegExp para criar uma expressão regular a partir de uma sequência:

var re = new RegExp("a|b", "i");
// same as
var re = /a|b/i;
quiabo
fonte
1
Seria bom ter a ferramenta on-line com um campo de entrada
holms
61
Ao fazê-lo desta maneira, você deve escapar as barras invertidas, por exemplovar re = new RegExp("\\w+");
JD Smith
12
@holms regex101.com é uma grande ferramenta online regex bem
Fran Herrero
2
Levei um tempo para ver que não há barra ao final necessário
Gerfried
2
@ JDSmith eu não quis dizer isso no seu exemplo. Eu quis dizer que você precisa escapar de aspas duplas se quiser que elas façam parte da regex, desde que sejam codificadas. Obviamente, nada disso se aplica se a string estiver em uma variável como uma <input>tag HTML. var re = new RegExp("\"\\w+\"");é um exemplo de um regex codificado usando o construtor RegExp e é necessário o escape das aspas duplas . O que quero dizer com uma string em uma variável é que você pode fazer var re = new RegExp(str);e strpode conter aspas duplas ou barras invertidas sem problemas.
Luis Paulo
66
var flags = inputstring.replace(/.*\/([gimy]*)$/, '$1');
var pattern = inputstring.replace(new RegExp('^/(.*?)/'+flags+'$'), '$1');
var regex = new RegExp(pattern, flags);

ou

var match = inputstring.match(new RegExp('^/(.*?)/([gimy]*)$'));
// sanity check here
var regex = new RegExp(match[1], match[2]);
Anônimo
fonte
Você deve considerar que uma entrada inválida como /\/é reconhecida.
Gumbo
8
Ou deixe o construtor RegExp falhar, "seguindo \ na expressão regular", em vez de escrever um analisador complicado.
Anônimo
21

Aqui está uma frase: str.replace(/[|\\{}()[\]^$+*?.]/g, '\\$&')

Eu o peguei no módulo NPM de escape-string-regexp .

Experimentando:

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

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

Usando literais de modelo marcados com suporte a sinalizadores:

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
fonte
15

Use o construtor de objeto JavaScript RegExp .

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

Você pode passar sinalizadores como um segundo argumento de string para o construtor. Veja a documentação para detalhes.

Ayman Hourieh
fonte
9

No meu caso, a entrada do usuário algumas vezes foi cercada por delimitadores e às vezes não. por isso adicionei outro caso ..

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
fonte
3
você sempre pode usar a .split()função em vez de uma longa seqüência de caracteres regex. regParts = inputstring.split('/')isso criaria regParts[1]a sequência de caracteres regex e regParts[2]os delimitadores (assumindo que a configuração do regex seja /.../gim). Você pode verificar se há delimitadores com regParts[2].length < 0.
precisa saber é o seguinte
3

Sugiro que você também adicione caixas de seleção separadas ou um campo de texto para os sinalizadores especiais. Dessa forma, fica claro que o usuário não precisa adicionar nenhum //. No caso de uma substituição, forneça dois campos de texto. Isso tornará sua vida muito mais fácil.

Por quê? Porque, caso contrário, alguns usuários adicionarão //, enquanto outros não. E alguns cometerão um erro de sintaxe. Então, depois de remover os //'s, você pode acabar com uma regex sintaticamente válida que não é nada parecida com o que o usuário pretendia, levando a um comportamento estranho (da perspectiva do usuário).

Stephan202
fonte
2

Isso funcionará também quando a string for inválida ou não contiver sinalizadores etc.:

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
fonte
2

Se você realmente deseja converter uma string em uma regex, tente usar a seguinte função:

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

Você pode usá-lo assim:

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

Para referência, aqui está a versão formatada e mais moderna:

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
fonte
1

Graças às respostas anteriores, esses blocos servem bem como uma solução de uso geral para aplicar uma string configurável em um RegEx .. para filtrar texto:

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
fonte
1

Você pode pedir sinalizadores usando caixas de seleção e fazer algo assim:

var userInput = formInput;
var flags = '';
if(formGlobalCheckboxChecked) flags += 'g';
if(formCaseICheckboxChecked) flags += 'i';
var reg = new RegExp(userInput, flags);
Pim Jager
fonte
olhares como RegEx está faltando à direita p .. Stack não me deixou fazer uma edição 1 caráter
Gene Bo
-3

Eu uso evalpara resolver este problema.

Por exemplo:

    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
fonte
3
eval em userInput é um risco de segurança louca
Samuel Faure
1
senhor mesas de bobby!
Luiz Felipe