2010-10-03 5 views
1

curieux de savoir si cela peut être simplifié ...Comment simplifier cette méthode (en séparant les guillemets sans guillemets, sans crochets, sans échappement)?

internal static IEnumerable<string> Split(string str, char sep = ',') 
{ 
    int lastIndex = 0; 
    bool quoted = false; 
    bool escaped = false; 
    bool bracketed = false; 
    char lastQuote = '\0'; 

    for (int i = 0; i < str.Length; ++i) 
    { 
     if (str[i] == '[') 
     { 
      if (!quoted && !escaped) 
       bracketed = true; 
      escaped = false; 
     } 
     else if (str[i] == ']') 
     { 
      if (!quoted && !escaped) 
       bracketed = false; 
      escaped = false; 
     } 
     else if (str[i] == '\\') 
     { 
      escaped = !escaped; 
     } 
     else if (str[i] == '"' || str[i] == '\'') 
     { 
      if (!escaped) 
      { 
       if (quoted) 
       { 
        if (lastQuote == str[i]) 
         quoted = false; 
       } 
       else 
       { 
        quoted = true; 
        lastQuote = str[i]; 
       } 
      } 
      escaped = false; 
     } 
     else if (str[i] == sep) 
     { 
      if (!quoted && !escaped && !bracketed) 
      { 
       yield return str.Substring(lastIndex, i - lastIndex); 
       lastIndex = i + 1; 
      } 
      escaped = false; 
     } 
     else 
     { 
      escaped = false; 
     } 
    } 

    yield return str.Substring(lastIndex); 
} 

A écrit cette méthode pour diviser sur des virgules qui ne sont pas à l'intérieur [], ne sont pas traités et ne sont pas échappés. Est-ce que c'est un problème intrinsèquement difficile, ou est-ce que j'ai pris une approche stupide?

Entrée:

foreach(var sel in SharpQuery.SplitCommas("\"comma, in quotes\", comma[in,brackets], comma[in \"quotes, and brackets\"], \"woah, 'nelly,' \\\"now you,re [talking, crazy\\\"\"")) { 
    Console.WriteLine(sel); 
} 

Sortie prévue:

"comma, in quotes" 
comma[in,brackets] 
comma[in "quotes, and brackets"] 
"woah, 'nelly,' \"now you,re [talking, crazy\"" 
+0

Avez-vous pensé à regex ?? – jimplode

+0

Le code que vous avez produit peut-être pas le plus concis ou élégant est * maintenable * - ce qui (à mon avis) est l'une des choses les plus importantes pour le code à être. Ce code cause-t-il des problèmes? Sinon, je le laisserais seul. – ChrisF

+0

@jimplode: Oui, et je crois que ce serait une approche terrible, si c'est faisable. @ChrisF: Semble fonctionner dans les tests que j'ai courus. Je ne suis pas trop inquiet à propos de l'optimisation, je me demandais simplement si elle pourrait être simplifiée davantage ... elle souffre d'un peu de redondance de code et d'autres choses. – mpen

Répondre

6

Un peu d'un choix difficile à garder votre état d'automate. J'utiliserais une seule variable ou une pile dans ce cas. Donc, votre état actuel est toujours stateStack.Peek(). Facile à lire. Facile à gérer les états imbriqués.

edit: heres un exemple rapide. Je suis sûr que vous pouvez l'étendre pour ajouter la gestion des erreurs et les spécificités de vos règles.

enum ParserState 
    { 
     Text, 
     Bracketed, 
     Quoted, 
     EscapChar, 
    } 

    internal static IEnumerable<string> Split(string str, char sep) 
    { 
     int lastIdx = 0; 
     char c; 
     ParserState s; 
     Stack<ParserState> state = new Stack<ParserState>(); 
     state.Push(ParserState.Text); 

     for (int i = 0; i < str.Length; i++) 
     { 
      c = str[i]; 
      s = state.Peek(); 

      if (s == ParserState.EscapChar 
       || (s == ParserState.Bracketed && c == ']') 
       || (s == ParserState.Quoted && c == '"')) 
      { 
       state.Pop(); 
      } 
      else if (c == '[') 
       state.Push(ParserState.Bracketed); 
      else if (c == '"') 
       state.Push(ParserState.Quoted); 
      else if (c == '\\') 
       state.Push(ParserState.EscapChar); 
      else if (s == ParserState.Text && c == sep) 
      { 
       yield return str.Substring(lastIdx, i - lastIdx); 
       lastIdx = i + 1; 
      } 
     } 
     yield return str.Substring(lastIdx); 
    } 
+0

Et poussez les deux guillemets et '[' '' 'sur? Qu'en est-il des caractères d'échappement? Souhaitez-vous les pousser aussi? – mpen

+0

bien, c'est plus une question pour vous. Supportez-vous les parenthèses imbriquées? Si vous le faites, appuyez sur 'ParserState.Bracketed' chaque fois que vous rencontrez' '[' '. Si ce n'est pas le cas, appuyez sur le premier et traitez les autres en texte clair. Idem pour la combinaison des parenthèses et des citations. –

+0

quant aux caractères d'échappement - oui je les pousserais. Fondamentalement, vous voulez que le 'state.Peek()' soit "c'est où je suis maintenant". Donc, si vous avez rencontré un caractère d'échappement, votre passage de boucle suivant doit en être informé. Aussi je suis un peu confus: Vous n'échappez pas à vos caractères d'échappement en sortie? Est-ce un oubli ou prévu? –

Questions connexes