web-dev-qa-db-fra.com

Comment faire correspondre plusieurs occurrences avec une expression régulière en JavaScript similaire à PHP preg_match_all ()?

J'essaie d'analyser des chaînes codées en URL composées de paires clé = valeur séparées par & ou &

Les éléments suivants ne correspondent qu'à la première occurrence, en séparant les clés et les valeurs en éléments de résultat distincts:

var result = mystring.match(/(?:&|&)?([^=]+)=([^&]+)/)

Les résultats pour la chaîne '1111342 = Adam% 20Franco & 348572 = Bob% 20Jones' seraient:

['1111342', 'Adam%20Franco']

L'utilisation de l'indicateur global, 'g', correspond à toutes les occurrences, mais renvoie uniquement les sous-chaînes totalement correspondantes, et non les clés et valeurs séparées:

var result = mystring.match(/(?:&|&)?([^=]+)=([^&]+)/g)

Les résultats pour la chaîne '1111342 = Adam% 20Franco & 348572 = Bob% 20Jones' seraient:

['1111342=Adam%20Franco', '&348572=Bob%20Jones']

Bien que je puisse scinder la chaîne sur & et séparer chaque paire clé/valeur individuellement, existe-t-il un moyen d'utiliser le support des expressions régulières de JavaScript pour faire correspondre plusieurs occurrences du modèle /(?:&|&)?([^=]+)=([^&]+)/, similaires à la fonction preg_match_all() de PHP?

Je vise un moyen d'obtenir des résultats avec les sous-matches séparés comme:

[['1111342', '348572'], ['Adam%20Franco', 'Bob%20Jones']]

ou 

[['1111342', 'Adam%20Franco'], ['348572', 'Bob%20Jones']]
154
Adam Franco

Je suggérerais une expression rationnelle alternative, utilisant des sous-groupes pour capturer le nom et la valeur des paramètres individuellement:

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 est un objet:

 {
 f: "q" 
 géocode: "" 
 hl: "de" 
 c'est-à-dire: "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 regex se décompose comme suit:

 (?: # groupe sans capture 
 \? | & # "?" ou "&" 
 (?: amp;)? # (autorisez "& amp;", pour les URL mal encodées en HTML ) 
) # termine le groupe sans capture 
 (# groupe 1 
 [^ = & #] + # tout caractère sauf "=", "&" ou "#"; au moins une fois 
 ) # end group 1 - ce sera le nom du paramètre 
 (?: # groupe non capturant 
 =? # an "=", facultatif 
 (# groupe 2 
 [^ & #] * # n'importe quel caractère sauf "&" ou "#"; autant de fois que nécessaire 
) # groupe d'extrémité 2 - ce sera la valeur du paramètre 
) # groupe d'extrémité non-capturant 
153
Tomalak

Vous devez utiliser le commutateur 'g' pour une recherche globale

var result = mystring.match(/(&|&)?([^=]+)=([^&]+)/g)
65
meouw

Si vous ne voulez pas vous fier à la "correspondance en aveugle" associée à l'exécution de la correspondance de style exec , JavaScript est livré avec une fonctionnalité correspondante, mais elle fait partie de l'appel de fonction replace , lorsqu’on utilise un "que faire avec les groupes de capture" fonction de traitement :

var data = {};

var getKeyValue = function(fullPattern, group1, group2, group3) {
  data[group2] = group3;
};

mystring.replace(/(?:&|&)?([^=]+)=([^&]+)/g, getKeyValue);

terminé.

Au lieu d'utiliser la fonction de traitement du groupe de capture pour renvoyer les chaînes de remplacement (pour le traitement de remplacement, le premier argument, appelé a ici, correspond à la correspondance de modèle complète, et les arguments suivants sont des groupes de capture individuels, b étant le groupe 1, c groupe 2, etc) nous prenons simplement les captures des groupes 2 et 3 et mettons en cache cette paire.

Ainsi, plutôt que d’écrire des fonctions d’analyse compliquées, rappelez-vous que la fonction "matchAll" en JavaScript est simplement "remplacer" par une fonction de gestionnaire de remplacement, ce qui permet d’obtenir une efficacité accrue en matière de correspondance des modèles.

37

Pour capturer des groupes, je suis habitué à utiliser preg_match_all dans PHP et j'ai essayé de reproduire ses fonctionnalités ici:

<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>
21
Aram Kocharyan

Définissez le modificateur g pour une correspondance globale:

/…/g
15
Gumbo

Source: https://developer.mozilla.org/ko/docs/Web/JavaScript/Reference/Global_Objects/RegExp/exec

Trouver des matchs successifs

Si votre expression régulière utilise l'indicateur "g", vous pouvez utiliser la méthode exec () plusieurs fois pour trouver des correspondances successives dans la même chaîne. Lorsque vous procédez ainsi, la recherche commence à la sous-chaîne de str spécifiée par la propriété lastIndex de l'expression régulière (test () fera également avancer la propriété lastIndex). Par exemple, supposons que vous ayez ce 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);
}

Ce script affiche le texte suivant:

Found abb. Next match starts at 3
Found ab. Next match starts at 912

Remarque: Ne placez pas le littéral d'expression régulière (ou le constructeur RegExp) dans la condition while, car cela créera une boucle infinie s'il existe une correspondance en raison de la réinitialisation de la propriété lastIndex à chaque itération. Assurez-vous également que l'indicateur global est défini ou une boucle se produira ici également.

11
KIM Taegyoon

Juste pour rester avec la question proposée comme indiqué par le titre, vous pouvez en fait parcourir chaque correspondance dans une chaîne en utilisant String.prototype.replace(). Par exemple, voici ce qui suit pour obtenir un tableau de tous les mots basé sur une expression régulière:

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 je voulais obtenir des groupes de capture ou même l'index de chaque correspondance, je pouvais le faire aussi. Ce qui suit montre comment chaque correspondance est renvoyée avec la correspondance entière, le 1er groupe de capture et l'index:

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?");

Après avoir exécuté ce qui précède, words sera comme suit:

[
  {
    "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
  }
]

Afin de faire correspondre plusieurs occurrences similaires à ce qui est disponible dans PHP avec preg_match_all , vous pouvez utiliser ce type de pensée pour créer la vôtre ou utiliser quelque chose comme YourJS.matchAll() . YourJS définit plus ou moins cette fonction comme suit:

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;
}
2
Chris West

Si quelqu'un (comme moi) a besoin de la méthode Tomalak avec prise en charge de tableaux (c'est-à-dire plusieurs sélections), la voici:

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

saisissez ?my=1&my=2&my=things

résultat 1,2,things (renvoyé précédemment uniquement: choses)

2
fedu

Utilisez 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"]]
1
jnnnnn

Si vous pouvez vous en tirer en utilisant map, voici une solution à quatre lignes:

var mystring = '1111342=Adam%20Franco&348572=Bob%20Jones';

var result = mystring.match(/(&|&amp;)?([^=]+)=([^&]+)/g) || [];
result = result.map(function(i) {
  return i.match(/(&|&amp;)?([^=]+)=([^&]+)/);
});

console.log(result);

Ce n'est pas joli, ce n'est pas efficace, mais au moins c'est compact. ;)

1
fboes

Pour éviter l'enfer des expressions rationnelles, vous pouvez trouver votre première correspondance, coupez un morceau puis essayez de trouver le suivant sur la sous-chaîne. En C #, cela ressemble à quelque chose comme ça, désolé je ne l’ai pas encore transféré à JavaScript.

        long count = 0;
        var remainder = data;
        Match match = null;
        do
        {
            match = _rgx.Match(remainder);
            if (match.Success)
            {
                count++;
                remainder = remainder.Substring(match.Index + 1, remainder.Length - (match.Index+1));
            }
        } while (match.Success);
        return count;
0
andrew pate

La scinder semble être la meilleure option pour moi:

'1111342=Adam%20Franco&348572=Bob%20Jones'.split('&').map(x => x.match(/(?:&|&amp;)?([^=]+)=([^&]+)/))
0
pguardiario

Pour capturer plusieurs paramètres utilisant le même nom, j'ai modifié la boucle while dans la méthode de Tomalak comme ceci:

  while (match = re.exec(url)) {
    var pName = decode(match[1]);
    var pValue = decode(match[2]);
    params[pName] ? params[pName].Push(pValue) : params[pName] = [pValue];
  }

entrée: ?firstname=george&lastname=bush&firstname=bill&lastname=clinton 

renvoie: {firstname : ["george", "bill"], lastname : ["bush", "clinton"]}

0
ivar

Eh bien ... j'ai eu un problème similaire ... Je veux une recherche incrémentielle/par paliers avec RegExp (Par exemple: démarrer la recherche ... faire un traitement ... continuer la recherche jusqu'au dernier match)

Après de nombreuses recherches sur Internet ... comme toujours (c'est devenu une habitude maintenant) Je me retrouve dans StackOverflow et j'ai trouvé la réponse ...

Ce qui n’est pas référé et l’important à mentionner est "lastIndex" Je comprends maintenant pourquoi l’objet RegExp implémente la propriété "lastIndex"

0
ZEE