web-dev-qa-db-fra.com

RegEx pour le format de données Taskwarrior correspondant

J'essaie d'analyser le type de chaîne suivant:

[key:"val" key2:"val2"]

où il y a des paires de clés arbitraires: "val" à l'intérieur. Je veux saisir le nom de la clé et la valeur. Pour ceux qui sont curieux, j'essaye d'analyser le format de base de données de guerrier de tâche.

Voici ma chaîne de test:

[description:"aoeu" uuid:"123sth"]

ce qui vise à souligner que tout peut être dans une clé ou une valeur en dehors de l'espace, pas d'espaces autour des deux points, et les valeurs sont toujours entre guillemets doubles.

En noeud, voici ma sortie:

[deuteronomy][gatlin][~]$ node
> var re = /^\[(?:(.+?):"(.+?)"\s*)+\]$/g
> re.exec('[description:"aoeu" uuid:"123sth"]');
[ '[description:"aoeu" uuid:"123sth"]',
  'uuid',
  '123sth',
  index: 0,
  input: '[description:"aoeu" uuid:"123sth"]' ]

Mais description:"aoeu" correspond également à ce modèle. Comment puis-je récupérer tous les matchs?

151
gatlin

Continuez à appeler re.exec(s) en boucle pour obtenir tous les résultats:

var re = /\s*([^[:]+):\"([^"]+)"/g;
var s = '[description:"aoeu" uuid:"123sth"]';
var m;

do {
    m = re.exec(s);
    if (m) {
        console.log(m[1], m[2]);
    }
} while (m);

Essayez-le avec ce JSFiddle: https://jsfiddle.net/7yS2V/

202
lawnsea

str.match(pattern), si pattern a l'indicateur global g, toutes les correspondances seront renvoyées sous forme de tableau.

Par exemple:

const str = 'All of us except @Emran, @Raju and @Noman was there';
console.log(
  str.match(/@\w*/g)
);
// Will log ["@Emran", "@Raju", "@Noman"]
96
Anis

Pour parcourir toutes les correspondances, vous pouvez utiliser la fonction replace:

var re = /\s*([^[:]+):\"([^"]+)"/g;
var s = '[description:"aoeu" uuid:"123sth"]';

s.replace(re, function(match, g1, g2) { console.log(g1, g2); });
84
Christophe

C'est une solution

var s = '[description:"aoeu" uuid:"123sth"]';

var re = /\s*([^[:]+):\"([^"]+)"/g;
var m;
while (m = re.exec(s)) {
  console.log(m[1], m[2]);
}

Ceci est basé sur la réponse de Lawnsea, mais plus court.

Notez que l'indicateur `g 'doit être défini pour déplacer le pointeur interne au-dessus des invocations.

54
lovasoa
str.match(/regex/g)

renvoie toutes les correspondances sous forme de tableau.

Si, pour une raison mystérieuse, vous avez besoin des informations supplémentaires fournies avec exec, vous pouvez le faire avec une fonction récursive au lieu d'une boucle comme suit (ce qui semble également plus cool).

function findMatches(regex, str, matches = []) {
   const res = regex.exec(str)
   res && matches.Push(res) && findMatches(regex, str, matches)
   return matches
}

// Usage
const matches = findMatches(/regex/g, str)

comme indiqué dans les commentaires précédents, il est important d’avoir g à la fin de la définition de l’expression régulière pour faire avancer le pointeur à chaque exécution.

14
noego

Basé sur la fonction d'Agus, mais je préfère renvoyer uniquement les valeurs de correspondance:

var bob = "> bob <";
function matchAll(str, regex) {
    var res = [];
    var m;
    if (regex.global) {
        while (m = regex.exec(str)) {
            res.Push(m[1]);
        }
    } else {
        if (m = regex.exec(str)) {
            res.Push(m[1]);
        }
    }
    return res;
}
var Amatch = matchAll(bob, /(&.*?;)/g);
console.log(Amatch);  // yeilds: [>, <]
9
bob

Les Iterables sont plus agréables:

const matches = (text, pattern) => ({
  [Symbol.iterator]: function * () {
    const clone = new RegExp(pattern.source, pattern.flags);
    let match = null;
    do {
      match = clone.exec(text);
      if (match) {
        yield match;
      }
    } while (match);
  }
});

Utilisation en boucle:

for (const match of matches('abcdefabcdef', /ab/g)) {
  console.log(match);
}

Ou si vous voulez un tableau:

[ ...matches('abcdefabcdef', /ab/g) ]
7
sdgfsdh

Nous commençons enfin à voir une fonction matchAll intégrée, voir ici pour la description et le tableau de compatibilité . Il semble qu'à compter d'avril 2019, Chrome et Firefox sont pris en charge, mais pas IE, Edge, Opera ou Node.js. On dirait que c'était rédigé en décembre 2018 alors laissez-lui un peu de temps pour atteindre tous les navigateurs, mais j'espère qu'il y parviendra.

La fonction intégrée matchAll est agréable car elle renvoie un itérable . Il retourne également des groupes de capture pour chaque match! Pour que vous puissiez faire des choses comme

// get the letters before and after "o"
let matches = "stackoverflow".matchAll(/(\w)o(\w)/g);

for (match of matches) {
    console.log("letter before:" + match[1]);
    console.log("letter after:" + match[2]);
}

arrayOfAllMatches = [...matches]; // you can also turn the iterable into an array

Il semble également que chaque objet de correspondance utilise le même format que match() . Ainsi, chaque objet est un tableau des groupes de correspondance et de capture, avec trois propriétés supplémentaires index, input et groups. Donc, il ressemble à:

[<match>, <group1>, <group2>, ..., index: <match offset>, input: <original string>, groups: <named capture groups>]

Pour plus d'informations sur matchAll, il existe également un page des développeurs Google . Il existe également polyfills/shims disponibles.

6
woojoo666

Voici ma fonction pour obtenir les matchs:

function getAllMatches(regex, text) {
    if (regex.constructor !== RegExp) {
        throw new Error('not RegExp');
    }

    var res = [];
    var match = null;

    if (regex.global) {
        while (match = regex.exec(text)) {
            res.Push(match);
        }
    }
    else {
        if (match = regex.exec(text)) {
            res.Push(match);
        }
    }

    return res;
}

var regex = /abc|def|ghi/g;
var res = getAllMatches(regex, 'abcdefghi');

res.forEach(function (item) {
    console.log(item[0]);
});
4
Agus Syahputra

Si votre système (Chrome/Node.js/Firefox) prend en charge ES9, utilisez la nouvelle fonction a_string.matchAll(regex). Si vous avez un système plus ancien, voici une fonction pour copier et coller facilement

function findAll(regexPattern, sourceString) {
    let output = []
    let match
    // make sure the pattern has the global flag
    let regexPatternWithGlobal = RegExp(regexPattern,"g")
    while (match = regexPatternWithGlobal.exec(sourceString)) {
        // get rid of the string copy
        delete match.input
        // store the match data
        output.Push(match)
    } 
    return output
}

exemple d'utilisation:

console.log(   findAll(/blah/g,'blah1 blah2')   ) 

les sorties:

[ [ 'blah', index: 0 ], [ 'blah', index: 6 ] ]
3
Jeff Hykin

Depuis ES9, il existe désormais un moyen plus simple et plus performant d'obtenir toutes les correspondances, ainsi que des informations sur les groupes de capture et leur index:

const string = 'Mice like to dice rice';
const regex = /.ice/gu;
for(const match of string.matchAll(regex)) {
    console.log(match);
}

// ["souris", index: 0, entrée: "les souris aiment couper le riz en dés", groupes: indéfini]

// ["dés", index: 13, entrée: "les souris aiment couper le riz en dés", groupes: indéfini]

// ["rice", index: 18, entrée: "les souris aiment couper le riz en dés", groupes: indéfini]

Il est actuellement pris en charge par Chrome, Firefox, Opera. En fonction du moment où vous lisez ceci, cochez ce lien pour voir son support actuel.

1
iuliu.net

Je recommanderais certainement d'utiliser la fonction String.match () et de créer un RegEx approprié. Mon exemple est avec une liste de chaînes, ce qui est souvent nécessaire lors de la numérisation des entrées utilisateur pour des mots-clés et des phrases.

    // 1) Define keywords
    var keywords = ['Apple', 'orange', 'banana'];

    // 2) Create regex, pass "i" for case-insensitive and "g" for global search
    regex = new RegExp("(" + keywords.join('|') + ")", "ig");
    => /(Apple|orange|banana)/gi

    // 3) Match it against any string to get all matches 
    "Test string for ORANGE's or apples were mentioned".match(regex);
    => ["ORANGE", "Apple"]

J'espère que cela t'aides!

0
Sebastian Scholl

Voici une solution à une ligne sans boucle while.

L'ordre est conservé dans la liste résultante.

Les inconvénients potentiels sont

  1. Il clone la regex pour chaque match.
  2. Le résultat est sous une forme différente de celle des solutions attendues. Vous devrez les traiter une fois de plus.
let re = /\s*([^[:]+):\"([^"]+)"/g
let str = '[description:"aoeu" uuid:"123sth"]'

(str.match(re) || []).map(e => RegExp(re.source, re.flags).exec(e))

[ [ 'description:"aoeu"',
    'description',
    'aoeu',
    index: 0,
    input: 'description:"aoeu"',
    groups: undefined ],
  [ ' uuid:"123sth"',
    'uuid',
    '123sth',
    index: 0,
    input: ' uuid:"123sth"',
    groups: undefined ] ]
0
Jae Won Jang

Utilisez ceci...

var all_matches = your_string.match(re);
console.log(all_matches)

Cela renverra un tableau de tous les matchs ... Cela fonctionnerait très bien ... Mais souvenez-vous que les groupes ne seront pas pris en compte ... Cela renverra simplement les matchs complets ...

0
Subham Debnath

Mon hypothèse est que s'il y avait des cas Edge tels que des espaces supplémentaires ou manquants, cette expression avec moins de limites pourrait également être une option:

^\s*\[\s*([^\s\r\n:]+)\s*:\s*"([^"]*)"\s*([^\s\r\n:]+)\s*:\s*"([^"]*)"\s*\]\s*$

Si vous souhaitez explorer/simplifier/modifier l'expression, cela a été expliqué dans le panneau en haut à droite de regex101.com . Si vous le souhaitez, vous pouvez également regarder dans ce lien , comment cela correspondrait avec certains exemples d'entrées.


Tester

const regex = /^\s*\[\s*([^\s\r\n:]+)\s*:\s*"([^"]*)"\s*([^\s\r\n:]+)\s*:\s*"([^"]*)"\s*\]\s*$/gm;
const str = `[description:"aoeu" uuid:"123sth"]
[description : "aoeu" uuid: "123sth"]
[ description : "aoeu" uuid: "123sth" ]
 [ description : "aoeu"   uuid : "123sth" ]
 [ description : "aoeu"uuid  : "123sth" ] `;
let m;

while ((m = regex.exec(str)) !== null) {
    // This is necessary to avoid infinite loops with zero-width matches
    if (m.index === regex.lastIndex) {
        regex.lastIndex++;
    }
    
    // The result can be accessed through the `m`-variable.
    m.forEach((match, groupIndex) => {
        console.log(`Found match, group ${groupIndex}: ${match}`);
    });
}

Circuit RegEx

jex.im visualise les expressions régulières:

enter image description here

0
Emma

Cela ne vous aidera pas vraiment à résoudre votre problème plus complexe, mais je le publie quand même parce que c'est une solution simple pour les personnes qui ne font pas de recherche globale comme vous.

J'ai simplifié l'expression rationnelle dans la réponse pour qu'elle soit plus claire (ce n'est pas une solution à votre problème exact).

var re = /^(.+?):"(.+)"$/
var regExResult = re.exec('description:"aoeu"');
var purifiedResult = purify_regex(regExResult);

// We only want the group matches in the array
function purify_regex(reResult){

  // Removes the Regex specific values and clones the array to prevent mutation
  let purifiedArray = [...reResult];

  // Removes the full match value at position 0
  purifiedArray.shift();

  // Returns a pure array without mutating the original regex result
  return purifiedArray;
}

// purifiedResult= ["description", "aoeu"]

Cela semble plus verbeux que ce n'est à cause des commentaires, voici à quoi ça ressemble sans commentaires

var re = /^(.+?):"(.+)"$/
var regExResult = re.exec('description:"aoeu"');
var purifiedResult = purify_regex(regExResult);

function purify_regex(reResult){
  let purifiedArray = [...reResult];
  purifiedArray.shift();
  return purifiedArray;
}

Notez que tous les groupes qui ne correspondent pas seront répertoriés dans le tableau sous la forme de valeurs undefined.

Cette solution utilise l'opérateur de propagation ES6 pour purifier le tableau de valeurs spécifiques à une expression rationnelle. Vous devrez exécuter votre code via Babel si vous souhaitez bénéficier du support IE11.

0
Daniel Tonon