2008-10-09 63 views

Respuesta

152

Aquí es cómo crear una expresión regular sin utilizar la sintaxis literal de expresión regular. Esto le permite hacer la manipulación de cadenas arbitrario antes de que se convierte en un objeto de expresión regular:

var segment_part = "some bit of the regexp"; 
var pattern = new RegExp("some regex segment" + /*comment here */ 
       segment_part + /* that was defined just now */ 
       "another segment"); 

Si tiene dos literales de expresiones regulares, de hecho, puede concatenar con esta técnica:

var expression_one = /foo/; 
var expression_two = /bar/; 
var expression_three = new RegExp(expression_one.source + expression_two.source); 

No es del todo una buena solución, ya que se pierden los indicadores que se establecieron en expression_one y expression_two, y es más prolijo que simplemente tener la expresión uno y dos siendo cadenas literales en lugar de expresiones regulares literales.

+13

No es para ser grosero pero, creo que estaba claro que lo sé, estaba preguntando específicamente si se puede hacer con el literal. – eyelidlessness

+90

Stackoverflow no es solo para usted y para mí, está diseñado para ser buscado y utilizado como un archivo de preguntas y respuestas. Di un ejemplo de código adecuado para que alguien con una pregunta similar pueda ver fácilmente una solución adecuada. – Jerub

+1

var regex1 =/asdf /; var regex2 =/qwerty /; var regex3 = new RegExp (regex1 + regex2); ///\/asdf \/\/qwerty \ // – eyelidlessness

0

Vas a tener que utilizar las nuevas RegExp -)

0

No, la forma literal no es compatible. Tendrás que usar RegExp.

-1

Yo prefiero usar eval('your expression') porque no agrega el / en cada extremo / que ='new RegExp' hace.

10

No estoy del todo de acuerdo con la opción "eval".

var xxx = /abcd/; 
var yyy = /efgh/; 
var zzz = new RegExp(eval(xxx)+eval(yyy)); 

dará "// // ABCD EFGH //", que no es el resultado que se pretende.

Uso de fuente como

var zzz = new RegExp(xxx.source+yyy.source); 

dará "/ abcdefgh /" y que es correcta.

Lógicamente no hay necesidad de EVALUAR, usted conoce su EXPRESIÓN. Solo necesita su FUENTE o cómo está escrita, no necesariamente su valor. En cuanto a las banderas, solo necesita usar el argumento opcional de RegExp.

En mi situación, corro en el problema de^y $ siendo usado en varias expresiones que estoy tratando de concatenar juntas! Esas expresiones son filtros gramaticales utilizados en todo el programa. Ahora quiero usar algunos de ellos para manejar el caso de PREPOSICIONES. Es posible que deba "cortar" las fuentes para eliminar el inicio y el final^(y/o) $ :) Saludos, Alex.

+0

Me gusta el uso de la fuente-propiedad. Si tú, como yo, usas jslint, te molestará si haces algo como esto: 'var regex =" \. \ .. * "' –

0

Sería preferible utilizar la sintaxis literal con la mayor frecuencia posible. Es más corto, más legible, y no necesita comillas de escape o copias de seguridad de doble escape. Desde "JavaScript Patterns", Stoyan Stefanov 2010.

Pero usar New puede ser la única forma de concatenar.

Me gustaría evitar la evaluación. No es seguro.

+1

Creo que las expresiones regulares complejas son más legibles cuando están fragmentadas y comentadas como en la pregunta. – Sam

15

Concatenar expresiones regulares al azar objetos puede tener algunos efectos secundarios adversos.Usar la RegExp.source lugar:

var r1 = /abc/g; 
var r2 = /def/; 
var r3 = new RegExp(r1.source + r2.source, 
        (r1.global ? 'g' : '') 
        + (r1.ignoreCase ? 'i' : '') + 
        (r1.multiline ? 'm' : '')); 
var m = 'test that abcdef and abcdef has a match?'.match(r3); 
// m should contain 2 matches 

Esto también le dará la capacidad de retener los Indicadores de expresiones regulares de una expresión regular anterior utilizando las banderas RegExp estándar.

jsFiddle

+5

Le doy crédito por la recombinación de banderas en mi solución. –

+0

Esto se puede mejorar usando 'RegExp.prototype.flags' –

1

utilizar el constructor con 2 params y evitar el problema de arrastre '/':

var re_final = new RegExp("\\" + ".", "g"); // constructor can have 2 params! 
console.log("...finally".replace(re_final, "!") + "\n" + re_final + 
    " works as expected...");     // !!!finally works as expected 

         // meanwhile 

re_final = new RegExp("\\" + "." + "g");    // appends final '/' 
console.log("... finally".replace(re_final, "!")); // ...finally 
console.log(re_final, "does not work!");    // does not work 
6

Problema Si la expresión regular contiene grupos back-juego como \ 1.

var r = /(a|b)\1/ // Matches aa, bb but nothing else. 
var p = /(c|d)\1/ // Matches cc, dd but nothing else. 

Entonces solo contaminar las fuentes no funcionará. De hecho, la combinación de los dos es:

var rp = /(a|b)\1(c|d)\1/ 
rp.test("aadd") // Returns false 

La solución: En primer lugar se cuenta el número de grupos que emparejan en la primera expresión regular, a continuación, por cada ficha de back-coincidente en la segunda, que se incrementará por el número de grupos coincidentes.

function concatenate(r1, r2) { 
    var count = function(r, str) { 
    return str.match(r).length; 
    } 
    var numberGroups = /([^\\]|^)(?=\((?!\?:))/g; // Home-made regexp to count groups. 
    var offset = count(numberGroups, r1.source);  
    var escapedMatch = /[\\](?:(\d+)|.)/g;  // Home-made regexp for escaped literals, greedy on numbers. 
    var r2newSource = r2.source.replace(escapedMatch, function(match, number) { return number?"\\"+(number-0+offset):match; }); 
    return new RegExp(r1.source+r2newSource, 
     (r1.global ? 'g' : '') 
     + (r1.ignoreCase ? 'i' : '') 
     + (r1.multiline ? 'm' : '')); 
} 

prueba:

var rp = concatenate(r, p) // returns /(a|b)\1(c|d)\2/ 
rp.test("aadd") // Returns true 
+0

¿Se puede mejorar la función para que funcione con cualquier tamaño? ¡Gracias! – paulkon

+2

Sí (no voy a modificarlo aquí sin embargo). Esta función es asociativa, por lo que puede usar el siguiente código: 'function concatenateList() {var res = arguments [0]; for (var i = 1; i

2

A condición de que:

  • que sabe lo que hace en su expresión regular;
  • tiene muchas piezas de expresiones regulares para formar un patrón y usarán la misma bandera;
  • le resulta más legible para separar los trozos de pequeños patrones en una matriz;
  • también desea poder comentar cada parte para el siguiente desarrollador o usted mismo más tarde;
  • Prefiere simplificar visualmente su expresión regular como /this/g en lugar de new RegExp('this', 'g');
  • está bien que ensamble la expresión regular en un paso adicional en lugar de tenerla en una pieza desde el principio;

entonces es posible que gustaría escribir de esta manera:

var regexParts = 
    [ 
     /\b(\d+|null)\b/,// Some comments. 
     /\b(true|false)\b/, 
     /\b(new|getElementsBy(?:Tag|Class|)Name|arguments|getElementById|if|else|do|null|return|case|default|function|typeof|undefined|instanceof|this|document|window|while|for|switch|in|break|continue|length|var|(?:clear|set)(?:Timeout|Interval))(?=\W)/, 
     /(\$|jQuery)/, 
     /many more patterns/ 
    ], 
    regexString = regexParts.map(function(x){return x.source}).join('|'), 
    regexPattern = new RegExp(regexString, 'g'); 

a continuación, puede hacer algo como:

string.replace(regexPattern, function() 
{ 
    var m = arguments, 
     Class = ''; 

    switch(true) 
    { 
     // Numbers and 'null'. 
     case (Boolean)(m[1]): 
      m = m[1]; 
      Class = 'number'; 
      break; 

     // True or False. 
     case (Boolean)(m[2]): 
      m = m[2]; 
      Class = 'bool'; 
      break; 

     // True or False. 
     case (Boolean)(m[3]): 
      m = m[3]; 
      Class = 'keyword'; 
      break; 

     // $ or 'jQuery'. 
     case (Boolean)(m[4]): 
      m = m[4]; 
      Class = 'dollar'; 
      break; 

     // More cases... 
    } 

    return '<span class="' + Class + '">' + m + '</span>'; 
}) 

En mi caso particular (un editor de código-espejo), es mucho más fácil realizar una gran expresión regular, en lugar de muchas sustituciones como seguir, ya que cada vez que reemplazo con una etiqueta html para envolver una expresión, el siguiente patrón será más difícil de orientar sin afectar la etiqueta html misma (y sin la bien de búsqueda hacia atrás que, lamentablemente, no es compatible con JavaScript):

.replace(/(\b\d+|null\b)/g, '<span class="number">$1</span>') 
.replace(/(\btrue|false\b)/g, '<span class="bool">$1</span>') 
.replace(/\b(new|getElementsBy(?:Tag|Class|)Name|arguments|getElementById|if|else|do|null|return|case|default|function|typeof|undefined|instanceof|this|document|window|while|for|switch|in|break|continue|var|(?:clear|set)(?:Timeout|Interval))(?=\W)/g, '<span class="keyword">$1</span>') 
.replace(/\$/g, '<span class="dollar">$</span>') 
.replace(/([\[\](){}.:;,+\-?=])/g, '<span class="ponctuation">$1</span>') 
0

Se podría hacer algo como:

function concatRegex(...segments) { 
    return new RegExp(segments.join('')); 
} 

Los segmentos serían cuerdas (en lugar de literales de expresiones regulares) aprobada en argumentos tan separadas.

Cuestiones relacionadas