regulares - ¿Cómo puedo unir múltiples ocurrencias con una expresión regular en JavaScript similar a la preg_match_all() de PHP?
javascript regex example (13)
Estoy intentando analizar cadenas codificadas en URL que están compuestas de pares de clave = valor separados por &
y &
.
Lo siguiente solo coincidirá con la primera ocurrencia, separando las claves y valores en elementos de resultado separados:
var result = mystring.match(/(?:&|&)?([^=]+)=([^&]+)/)
Los resultados para la cadena ''1111342 = Adam% 20Franco & 348572 = Bob% 20Jones'' serían:
[''1111342'', ''Adam%20Franco'']
Al usar el indicador global, ''g'' coincidirá con todas las ocurrencias, pero solo devolverá las cadenas secundarias totalmente coincidentes, no las claves y valores separados:
var result = mystring.match(/(?:&|&)?([^=]+)=([^&]+)/g)
Los resultados para la cadena ''1111342 = Adam% 20Franco & 348572 = Bob% 20Jones'' serían:
[''1111342=Adam%20Franco'', ''&348572=Bob%20Jones'']
Si bien podría dividir la secuencia y separar cada par de clave / valor individualmente, ¿hay alguna manera de utilizar el soporte de expresiones regulares de JavaScript para que coincida con múltiples apariciones del patrón /(?:&|&)?([^=]+)=([^&]+)/
similar a la función preg_match_all()
PHP?
Estoy buscando alguna manera de obtener resultados con los sub-partidos separados como:
[[''1111342'', ''348572''], [''Adam%20Franco'', ''Bob%20Jones'']]
o
[[''1111342'', ''Adam%20Franco''], [''348572'', ''Bob%20Jones'']]
Bueno ... tuve un problema similar ... quiero una búsqueda incremental / por pasos con RegExp (por ejemplo: iniciar búsqueda ... hacer algún procesamiento ... continuar la búsqueda hasta el último partido)
Después de mucha búsqueda en Internet ... como siempre (esto se está convirtiendo en un hábito ahora) termino en y encontré la respuesta ...
Lo que no se refiere y lo que importa es " lastIndex
" Ahora entiendo por qué el objeto RegExp implementa la propiedad " lastIndex
"
Dividirlo parece ser la mejor opción para mí:
''1111342=Adam%20Franco&348572=Bob%20Jones''.split(''&'').map(x => x.match(/(?:&|&)?([^=]+)=([^&]+)/))
Establezca el modificador g
para una coincidencia global:
/…/g
Fuente: https://developer.mozilla.org/ko/docs/Web/JavaScript/Reference/Global_Objects/RegExp/exec
Encontrar partidos sucesivos
Si su expresión regular usa el indicador "g", puede usar el método exec () varias veces para buscar coincidencias sucesivas en la misma cadena. Cuando lo hace, la búsqueda comienza en la subcadena de str especificada por la propiedad lastIndex de la expresión regular (test () también avanzará la propiedad lastIndex). Por ejemplo, supongamos que tiene este script:
var myRe = /ab*/g;
var str = ''abbcdefabh'';
var myArray;
while ((myArray = myRe.exec(str)) !== null) {
var msg = ''Found '' + myArray[0] + ''. '';
msg += ''Next match starts at '' + myRe.lastIndex;
console.log(msg);
}
Este script muestra el siguiente texto:
Found abb. Next match starts at 3
Found ab. Next match starts at 912
Nota: No coloque el literal de la expresión regular (o el constructor RegExp) dentro de la condición while o se creará un bucle infinito si existe una coincidencia debido a que la propiedad lastIndex se restablece en cada iteración. También asegúrese de que la bandera global esté configurada o que un bucle ocurra aquí también.
Necesita usar el interruptor ''g'' para una búsqueda global
var result = mystring.match(/(&|&)?([^=]+)=([^&]+)/g)
Para capturar grupos, estoy acostumbrado a usar preg_match_all
en PHP y he intentado replicar su funcionalidad aquí:
<script>
// Return all pattern matches with captured groups
RegExp.prototype.execAll = function(string) {
var match = null;
var matches = new Array();
while (match = this.exec(string)) {
var matchArray = [];
for (i in match) {
if (parseInt(i) == i) {
matchArray.push(match[i]);
}
}
matches.push(matchArray);
}
return matches;
}
// Example
var someTxt = ''abc123 def456 ghi890'';
var results = /[a-z]+(/d+)/g.execAll(someTxt);
// Output
[["abc123", "123"],
["def456", "456"],
["ghi890", "890"]]
</script>
Para capturar varios parámetros usando el mismo nombre, modifiqué el ciclo while en el método de Tomalak así:
while (match = re.exec(url)) {
var pName = decode(match[1]);
var pValue = decode(match[2]);
params[pName] ? params[pName].push(pValue) : params[pName] = [pValue];
}
entrada ?firstname=george&lastname=bush&firstname=bill&lastname=clinton
devoluciones: {firstname : ["george", "bill"], lastname : ["bush", "clinton"]}
Para seguir con la pregunta propuesta, tal como lo indica el título, puede iterar sobre cada coincidencia en una cadena usando String.prototype.replace()
. Por ejemplo, lo siguiente hace justamente eso para obtener una matriz de todas las palabras basadas en una expresión regular:
function getWords(str) {
var arr = [];
str.replace(//w+/g, function(m) {
arr.push(m);
});
return arr;
}
var words = getWords("Where in the world is Carmen Sandiego?");
// > ["Where", "in", "the", "world", "is", "Carmen", "Sandiego"]
Si quisiera obtener grupos de captura o incluso el índice de cada partido, podría hacerlo también. A continuación, se muestra cómo se devuelve cada coincidencia con la coincidencia completa, el primer grupo de captura y el índice:
function getWords(str) {
var arr = [];
str.replace(//w+(?=(.*))/g, function(m, remaining, index) {
arr.push({ match: m, remainder: remaining, index: index });
});
return arr;
}
var words = getWords("Where in the world is Carmen Sandiego?");
Después de ejecutar lo anterior, las words
serán las siguientes:
[
{
"match": "Where",
"remainder": " in the world is Carmen Sandiego?",
"index": 0
},
{
"match": "in",
"remainder": " the world is Carmen Sandiego?",
"index": 6
},
{
"match": "the",
"remainder": " world is Carmen Sandiego?",
"index": 9
},
{
"match": "world",
"remainder": " is Carmen Sandiego?",
"index": 13
},
{
"match": "is",
"remainder": " Carmen Sandiego?",
"index": 19
},
{
"match": "Carmen",
"remainder": " Sandiego?",
"index": 22
},
{
"match": "Sandiego",
"remainder": "?",
"index": 29
}
]
Para hacer coincidir varias ocurrencias similares a las disponibles en PHP con preg_match_all
, puede usar este tipo de pensamiento para hacer su propio o usar algo como YourJS.matchAll()
. YourJS define más o menos esta función de la siguiente manera:
function matchAll(str, rgx) {
var arr, extras, matches = [];
str.replace(rgx.global ? rgx : new RegExp(rgx.source, (rgx + '''').replace(/[/s/S]+///g , ''g'')), function() {
matches.push(arr = [].slice.call(arguments));
extras = arr.splice(-2);
arr.index = extras[0];
arr.input = extras[1];
});
return matches[0] ? matches : null;
}
Si alguien (como yo) necesita el método de Tomalak con soporte de matriz (es decir, selección múltiple), aquí está:
function getUrlParams(url) {
var re = /(?:/?|&(?:amp;)?)([^=&#]+)(?:=?([^&#]*))/g,
match, params = {},
decode = function (s) {return decodeURIComponent(s.replace(//+/g, " "));};
if (typeof url == "undefined") url = document.location.href;
while (match = re.exec(url)) {
if( params[decode(match[1])] ) {
if( typeof params[decode(match[1])] != ''object'' ) {
params[decode(match[1])] = new Array( params[decode(match[1])], decode(match[2]) );
} else {
params[decode(match[1])].push(decode(match[2]));
}
}
else
params[decode(match[1])] = decode(match[2]);
}
return params;
}
var urlParams = getUrlParams(location.search);
input ?my=1&my=2&my=things
resultado 1,2,things
(antes solo devuelto: cosas)
Si no quiere confiar en la "coincidencia ciega" que viene con la ejecución del estilo de ejecución exec
, JavaScript viene con la funcionalidad de combinar todo, pero es parte de la llamada de función de replace
cuando se usa un "qué hacer con la función de manejo de grupos de captura:
var data = {};
var getKeyValue = function(fullPattern, group1, group2, group3) {
data[group2] = group3;
};
mystring.replace(/(?:&|&)?([^=]+)=([^&]+)/g, getKeyValue);
hecho.
En lugar de usar la función de manejo de grupo de captura para devolver realmente cadenas de reemplazo (para reemplazar el manejo, la primera arg, llamada a
aquí, coincide con el patrón completo, y las subsiguientes args son grupos de captura individuales, en este caso b
es grupo 1, grupo c
2, etc.) simplemente tomamos las capturas de grupos 2 y 3, y almacenamos en caché ese par.
Entonces, en lugar de escribir funciones de análisis complicadas, recuerde que la función "matchall" en JavaScript es simplemente "reemplazar" con una función de controlador de reemplazo, y se puede tener mucha eficiencia de coincidencia de patrones.
Si puede salirse con la suya usando el map
esta es una solución de cuatro líneas:
var mystring = ''1111342=Adam%20Franco&348572=Bob%20Jones'';
var result = mystring.match(/(&|&)?([^=]+)=([^&]+)/g) || [];
result = result.map(function(i) {
return i.match(/(&|&)?([^=]+)=([^&]+)/);
});
console.log(result);
No es bonito, no es eficiente, pero al menos es compacto. ;)
Sugeriría una expresión regular alternativa, usando subgrupos para capturar el nombre y el valor de los parámetros individualmente:
function getUrlParams(url) {
var re = /(?:/?|&(?:amp;)?)([^=&#]+)(?:=?([^&#]*))/g,
match, params = {},
decode = function (s) {return decodeURIComponent(s.replace(//+/g, " "));};
if (typeof url == "undefined") url = document.location.href;
while (match = re.exec(url)) {
params[decode(match[1])] = decode(match[2]);
}
return params;
}
var result = getUrlParams("http://maps.google.de/maps?f=q&source=s_q&hl=de&geocode=&q=Frankfurt+am+Main&sll=50.106047,8.679886&sspn=0.370369,0.833588&ie=UTF8&ll=50.116616,8.680573&spn=0.35972,0.833588&z=11&iwloc=addr");
result
es un objeto:
{ f: "q" geocode: "" hl: "de" ie: "UTF8" iwloc: "addr" ll: "50.116616,8.680573" q: "Frankfurt am Main" sll: "50.106047,8.679886" source: "s_q" spn: "0.35972,0.833588" sspn: "0.370369,0.833588" z: "11" }
La expresión regular se divide de la siguiente manera:
(?: # non-capturing group /?|& # "?" or "&" (?:amp;)? # (allow "&", for wrongly HTML-encoded URLs) ) # end non-capturing group ( # group 1 [^=]+ # any character except "=", "&" or "#"; at least once ) # end group 1 - this will be the parameter''s name (?: # non-capturing group =? # an "=", optional ( # group 2 [^]* # any character except "&" or "#"; any number of times ) # end group 2 - this will be the parameter''s value ) # end non-capturing group
Use window.URL
:
> s = ''http://www.example.com/index.html?1111342=Adam%20Franco&348572=Bob%20Jones''
> u = new URL(s)
> Array.from(u.searchParams.entries())
[["1111342", "Adam Franco"], ["348572", "Bob Jones"]]