Chaîne de rupture après un mot spécifique et place des restes sur une nouvelle ligne (Regex)

Supposons que j'ai un champ de texte dans lequel un utilisateur peut envoyer des extraits de code. Je veux détecter quand un mot spécifique se produit dans la chaîne et ensuite faire quelque chose avec les mots / caractères qui viennent après ce mot.

Disons que nous avons une chaîne et qu'après le mot pyjama je veux commencer le reste du code sur une nouvelle ligne sans retrait. (Très similaire à la façon dont les embellisseurs de code fonctionnent.) La sortie sera rendue dans pre , donc je ne veux pas de balises ou d'autres balises HTML.

Il y a cependant des prises.

  1. Tout ce qui suit un mot ( pyjamas ) doit commencer sur une nouvelle ligne sur le même "niveau" (quantité égale d'onglets) comme ligne avant.
  2. Commas devrait toujours démarrer sur une nouvelle ligne et inverser le retrait avec un onglet
  3. Quand il y a un autre personnage, disons un point d'exclamation ! , Le code suivant doit commencer sur une nouvelle ligne avec un onglet en tant que retrait.

Exemple:

Entrée :

 Bananas! Apples and pears walk down pyjamas the street! and they say pyjamas hi to eachother, pyjamas But then! some one else comes pyjamas along pyjamas Who is he?, pyjamas I don't know who! he is pyjamas whatever,, 

Sortie :

 Bananas! Apples and pears walk down pyjamas the street! and they say pyjamas hi to eachother , pyjamas But then! some one else comes pyjamas along pyjamas Who is he? , pyjamas I don't know who! he is pyjamas whatever , , 

Je travaille avec jQuery, afin que vous puissiez l'utiliser si vous le souhaitez.

Voici un violon avec le code ci-dessus, afin que vous puissiez le tester. Mon résultat jusqu'à présent n'est pas génial du tout. (Tapez quelque chose dans la zone de texte, la sortie changera.) Étant donné que je ne suis actuellement que très informé avec regex, j'ai besoin d'aide.

Ce que j'ai jusqu'ici:

 var a = $("textarea").val(), b = a.split('!').join("!\n "), c = b.split('pyjamas').join("pyjamas \n"); $("textarea").keyup(function() { $("#output>pre").html(c); }); 

Voici une approche simple qui ne nécessite pas de fonctions récursives et pourrait même être effectuée sans expressions régulières (mais je les trouve pratiques ici).

 function indent(str) { var tabs = function(n) { return new Array(n+1).join('\t'); } var tokens = str.match(/!|,|pyjamas|(?:(?!pyjamas)[^!,])+/g); var depth = 0; var result = ''; for (var i = 0; i < tokens.length; ++i) { var token = tokens[i]; switch(token) { case '!': ++depth; result += token + '\n' + tabs(depth); break; case ',': --depth; result += '\n' + tabs(depth) + token; break; case 'pyjamas': result += token + '\n' + tabs(depth); break; default: result += token; break; } } return result; } 

Tout d'abord, nous définissons une fonction qui renvoie une chaîne de n onglets (pour plus de commodité).

Ensuite, nous divisons le processus en deux étapes. Tout d'abord, nous supprimons la chaîne – c'est-à-dire que nous la divisons ! pyjamas et autre chose. (Il y a une explication de la regex à la fin, mais vous pourriez également faire la tokenisation d'une autre manière). Ensuite, nous parcourons les jetons un à un en gardant le niveau d'indentation actuel en depth .

  • Si c'est un ! Nous augmentons la profondeur, imprimez le ! , Une ligne de rupture et les onglets.
  • Si c'est a , nous décrémentons la profondeur, imprimons une rupture de ligne, les onglets et ensuite,.
  • Si c'est un pyjamas , nous imprimons simplement cela et une pause de ligne et les onglets.
  • Si c'est autre chose, nous imprimons simplement ce jeton.

C'est tout. Vous voudrez peut-être ajouter une certaine vérification de la sérénité que la profondeur ne soit pas négative (c.-à-d., Vous avez plus que ! ) – actuellement, cela serait simplement rendu sans aucun onglet, mais vous devriez écrire en extra ! Après cela, rétablis la profondeur jusqu'à 1 . C'est assez facile à traiter, mais je ne sais pas quelles sont vos hypothèses ou vos exigences.

Il ne prend pas non plus soin des espaces supplémentaires après les pauses de ligne (voir l'édition à la fin).

Démonstration de travail.

Maintenant pour le regex:

 / ! # Match a literal ! | # OR , # Match a literal , | # OR pyjamas # Match pyjamas | # OR (?: # open a non-capturing group (?!pyjamas) # make sure that the next character is not the 'p' of 'pyjamas' [^!,] # match a non-!, non-, character )+ # end of group, repeat once or more (as often as possible) /g 

Le g pour trouver toutes les correspondances (par opposition à la première). ECMAScript 6 viendra avec un modificateur y , ce qui rendra la tokenisation encore plus facile – mais ce modificateur y est génial, c'est la propre invention de ECMAScript, alors que toute autre saveur qui fournit cette fonctionnalité utilise une ancre \G dans le motif.

Si certains des concepts les plus avancés de la regex ne vous connaissent pas, je vous renvoie à ce grand tutoriel:

  • Classes de caractères négatives
  • Groupes de non-capture
  • Lookaheads

MODIFIER:

Voici une version mise à jour qui corrige la mise en garde ci-dessus que j'ai mentionnée concernant les espaces après les sauts de ligne. À la fin du traitement, nous supprimons tous les espaces après les onglets avec:

 result = result.replace(/^(\t*)[ ]+/gm, '$1'); 

Le regex correspond au début d'une ligne, puis capture zéro ou plus d'onglets, puis autant d'espaces que possible. Les crochets autour de l'espace ne sont pas nécessaires mais améliorent la lisibilité. Le modificateur g est de nouveau à trouver toutes ces correspondances et m fait ^ correspond au début d'une ligne (par opposition au début de la chaîne). Dans la chaîne de remplacement $1 réfère à ce que nous avons capturé dans les parenthèses – c'est-à-dire tous ces onglets. Donc écrivez les onglets mais appliquez les espaces.

Démonstration de travail.

Pas si différent de la solution m.buettner, vous pouvez le faire en utilisant la méthode de remplacement:

 var lvl = 1; var res = str.replace(/(!)\s*|\s*(,)|(\bpyjamas)\s+/g, function (m, g1, g2, g3) { if (g1) return g1 + "\n" + Array(++lvl).join("\t"); if (g2) return "\n" + Array((lvl>1)?--lvl:lvl).join("\t") + g2; return g3 + "\n" + Array(lvl).join("\t"); }); console.log(res); 

L'idée est d'utiliser trois groupes de capture différents et de les tester dans la fonction de rappel. Selon le groupe de capture, le niveau est incrémenté ou décrémenté (le sol est au niveau 1). Lorsque le niveau est égal à 1 et qu'une virgule est trouvée, le niveau reste réglé sur 1. J'ai ajouté \s* et \s+ pour recadrer les espaces avant les virgules et après ! Et le pyjamas . Si vous ne voulez pas cela, vous pouvez l'enlever.

Avec votre code:

 $("#output>pre").html($("textarea").val()); $("textarea").keyup(function() { $("#output>pre").html(function() { var lvl = 1; return $("textarea").val().replace(/(!)\s*|\s*(,)|(\bpyjamas)\s+/g, function (m, g1, g2, g3) { if (g1) return g1 + "\n" + Array(++lvl).join("\t"); if (g2) return "\n" + Array((lvl>1)?--lvl:lvl).join("\t") + g2; return g3 + "\n" + Array(lvl).join("\t"); }); }); }); 

Remarque: il est probablement plus propre à définir une fonction que vous pouvez réutiliser plus tard.