2010-10-27 7 views
1

Je suis en train d'analyser un langage simple (formules Excel) pour les fonctions qu'il contient. Un nom de fonction doit commencer par n'importe quelle lettre, suivi d'un nombre quelconque de lettres/chiffres, et se terminer par un paren ouvert (pas d'espace entre les deux). Par exemple MyFunc(. La fonction peut contenir tous les arguments, y compris d'autres fonctions et doit se terminer par un paren ). Bien sûr, les maths au sein des parens sont autorisés =MyFunc((1+1)) et (1+1) ne devrait pas être détecté en tant que fonction car il échoue à la règle de fonction que je viens de décrire. Mon but est de reconnaître les appels de fonction de niveau supérieur dans une formule, d'identifier le nom de la fonction, d'extraire les arguments. Avec les arguments, je peux rechercher récursivement d'autres appels de fonction.Regex pour analyser les fonctions avec une profondeur arbitraire

En utilisant ce tutorial j'ai hacké les expressions rationnelles suivantes. Aucun ne semble faire l'affaire. Ils échouent tous les deux sur le cas de test collé ci-dessous.

Ce devrait travail mais complètement échoue:

(?<name>[a-z][a-z0-9]*\()(?<body>(?>[a-z][a-z0-9]*\((?<DEPTH>)|\)(?<-DEPTH>)|.?)*(?(DEPTH)(?!)))\) 

Cela fonctionne pour de nombreux cas de test, mais pas pour le cas de test ci-dessous. Je ne pense pas que l'air il gère les fonctions imbriquées juste pour correctement-paren ouvert/paren à proximité de l'imbrication:

(?<name>[a-z][a-z0-9]*\()(?<body>(?>\((?<DEPTH>)|\)(?<-DEPTH>)|.?)*(?(DEPTH)(?!)))\) 

Voici le test qui les brise tous:

=Date(Year(A$5),Month(A$5),1)-(Weekday(Date(Year(A$5),Month(A$5),1))-1)+{0;1;2;3;4;5}*7+{1,2,3,4,5,6,7}-1 

Cela devrait correspondre comme:

Date(ARGUMENTS1) 
Weekday(ARGUMENTS2) 
Where ARGUMENTS2 = Date(Year(A$5),Month(A$5),1) 

Au contraire, il correspond:

ARGUMENTS2 = Date(Year(A$5),Month(A$5),1)-1) 

J'utilise .net RegEx qui fournit de la mémoire externe.

+3

Cela ne peut pas être fait avec une expression régulière comme regex ne fonctionne qu'avec [langues régulières] (http://en.wikipedia.org/wiki/Regular_language). Vous devez utiliser un analyseur. Aussi, est ce devoir? – JoshD

+0

Je pense que cette approche n'a pas beaucoup de sens.Je préférerais écrire un analyseur pour cela. – steinar

+0

@JoshD - pourquoi n'est-ce pas une langue régulière? Quel principe enfreint-il? Je ne peux pas dire. Non, ce n'est pas un devoir (peut-on inverser -1?). Je suis un ingénieur logiciel professionnel et la solution est nécessaire pour contourner un bogue dans Excel. J'ai passé de nombreuses heures à essayer de le faire fonctionner avant de poster. – SFun28

Répondre

4

Ce qui est bien dans les capacités de regexes .NET. Voici une démonstration de travail:

using System; 
using System.Text.RegularExpressions; 

namespace Test 
{ 
    class Test 
    { 
    public static void Main() 
    { 
     Regex r = new Regex(@" 
     (?<name>[a-z][a-z0-9]*\() 
      (?<body> 
      (?> 
       \((?<DEPTH>) 
      | 
       \)(?<-DEPTH>) 
      | 
       [^()]+ 
      )* 
      (?(DEPTH)(?!)) 
     ) 
     \)", RegexOptions.IgnoreCase | RegexOptions.IgnorePatternWhitespace); 

     string formula = @"=Date(Year(A$5),Month(A$5),1)-(Weekday(Date(Year((A$5+1)),Month(A$5),1))-1)+{0;1;2;3;4;5}*7+{1,2,3,4,5,6,7}-1"; 

     foreach (Match m in r.Matches(formula)) 
     { 
     Console.WriteLine("{0}\n", m.Value); 
     } 
    } 
    } 
} 

sortie:

Date(Year(A$5),Month(A$5),1) 

Weekday(Date(Year((A$5+1)),Month(A$5),1))

Le principal problème avec votre regex est que vous incluez le nom de la fonction dans le cadre du match récursive - par exemple:

Name1(...Name2(...)...) 

N'importe quel open-paren qui n'a pas été précédé par le nom n'a pas été compté, parce qu'il a été apparié par l'alternative finale, |.?), et cela a jeté le solde avec le close-parens. Cela signifiait également que vous ne pouviez pas faire correspondre des formules telles que =MyFunc((1+1)), que vous avez mentionnées dans le texte mais qui ne figuraient pas dans l'exemple. (J'ai jeté dans un jeu supplémentaire de parens pour démontrer.)

EDIT: Voici la version avec prise en charge non significative, cité parens:

Regex r = new Regex(@" 
    (?<name>[a-z][a-z0-9]*\() 
     (?<body> 
     (?> 
      \((?<DEPTH>) 
     | 
      \)(?<-DEPTH>) 
     | 
      ""[^""]+"" 
     | 
      [^()""]+ 
     )* 
     (?(DEPTH)(?!)) 
    ) 
    \)", RegexOptions.IgnoreCase | RegexOptions.IgnorePatternWhitespace); 
+0

Ah! Merci! Je n'aurais pas compris ça moi-même. C'est la bonne solution. Merci de me croire que cela pourrait être fait avec Regex =) Ok ... Je vais jeter ça là-bas purement comme extra-crédit/absolument pas nécessaire/encore reconnaissant que vous m'avez débloqué: y at-il un moyen d'ignorer les parens dans citations? – SFun28

+0

dernier commentaire soumis avant complet. Donc, l'idée est que = Func ("Hel (o") analyse bien, je jette ça parce que j'ai le sentiment que quelqu'un posera cette même question à l'avenir.Pour ce qui me concerne, je peux garantir que les chaînes auront des parenthèses déséquilibrées – SFun28

+0

@ SFun28: Tout ce que vous avez à faire est d'ajouter une autre alternative pour faire correspondre les sections quotées complètes, puis exclure les guillemets ainsi que les parenthèses dans le final, * tout le reste * alternative: '(?> \\ ((? ) | \\) (? <-DEPTH>) | "" [^ ""] + "" | [^() ""] +) * ' –

0

Vous voudrez peut-être regarder ce lien: http://www.xtremevbtalk.com/archive/index.php/t-177848.html

+0

regardé le lien, mais cela ne résout pas mon problème. Je n'essaie pas d'utiliser Excel pour évaluer une fonction, j'essaie de décomposer des fonctions plus importantes dans le code. Cela devrait être possible sans charger Excel. – SFun28

Questions connexes