2008-09-10 6 views
7

Je chaîne comme ceLa meilleure façon d'analyser l'espace séparé Texte

/c SomeText\MoreText "Some Text\More Text\Lol" SomeText 

Je veux tokenizer il, mais je ne peux pas partager les espaces. J'ai trouvé un parser un peu moche qui fonctionne, mais je me demande si quelqu'un a un design plus élégant.

Ceci est en C# btw.

EDIT: Ma version laide, bien que moche, est O (N) et peut être plus rapide que l'utilisation d'un RegEx.

private string[] tokenize(string input) 
{ 
    string[] tokens = input.Split(' '); 
    List<String> output = new List<String>(); 

    for (int i = 0; i < tokens.Length; i++) 
    { 
     if (tokens[i].StartsWith("\"")) 
     { 
      string temp = tokens[i]; 
      int k = 0; 
      for (k = i + 1; k < tokens.Length; k++) 
      { 
       if (tokens[k].EndsWith("\"")) 
       { 
        temp += " " + tokens[k]; 
        break; 
       } 
       else 
       { 
        temp += " " + tokens[k]; 
       } 
      } 
      output.Add(temp); 
      i = k + 1; 
     } 
     else 
     { 
      output.Add(tokens[i]); 
     } 
    } 

    return output.ToArray();    
} 
+0

S'il vous plaît nous en dire plus sur ce que vous essayez d'accomplir, y compris la raison pour laquelle vous ne pouvez pas diviser sur les espaces. Ensuite, nous pouvons adapter nos réponses à votre situation. –

Répondre

16

Le terme informatique pour ce que vous faites est lexical analysis; lisez cela pour un bon résumé de cette tâche commune. En fonction de votre exemple, j'imagine que vous voulez que les espaces séparent vos mots, mais les guillemets doivent être traités comme un "mot" sans les guillemets.

La façon la plus simple de le faire est de définir un mot comme une expression régulière:

([^"^\s]+)\s*|"([^"]+)"\s* 

Cette expression indique qu'un « mot » est soit (1) non-citation, texte non blanc entouré un espace, ou (2) un texte sans citation entouré de guillemets (suivi d'un espace). Notez l'utilisation de parenthèses de capture pour mettre en évidence le texte souhaité. Armé de cette regex, votre algorithme est simple: recherchez votre mot pour le prochain "mot" tel que défini par les parenthèses de capture, et renvoyez-le. Répétez cela jusqu'à ce que vous manquiez de "mots".

Voici le code de travail le plus simple que j'ai pu trouver dans VB.NET. Notez que nous devons vérifier les deux groupes pour les données car il existe deux jeux de parenthèses de capture.

Dim token As String 
Dim r As Regex = New Regex("([^""^\s]+)\s*|""([^""]+)""\s*") 
Dim m As Match = r.Match("this is a ""test string""") 

While m.Success 
    token = m.Groups(1).ToString 
    If token.length = 0 And m.Groups.Count > 1 Then 
     token = m.Groups(2).ToString 
    End If 
    m = m.NextMatch 
End While 

Note 1: Will's réponse, ci-dessus, est la même idée que celle-ci. Espérons que cette réponse explique les détails derrière la scène un peu mieux :)

8

L'espace de noms Microsoft.VisualBasic.FileIO (en Microsoft.VisualBasic.dll) a une TextFieldParser vous pouvez utiliser pour diviser le texte delimeted de l'espace. Il gère les chaînes entre guillemets (c'est-à-dire, "ceci est un jeton", ceci est bien). Remarquez, juste parce que la DLL dit VisualBasic ne signifie pas que vous pouvez seulement l'utiliser dans un projet VB. Sa partie de l'ensemble du cadre.

0

Vous pouvez également rechercher des expressions régulières. Cela pourrait vous aider. Voici un exemple arraché de MSDN ...

using System; 
using System.Text.RegularExpressions; 

public class Test 
{ 

    public static void Main() 
    { 

     // Define a regular expression for repeated words. 
     Regex rx = new Regex(@"\b(?<word>\w+)\s+(\k<word>)\b", 
      RegexOptions.Compiled | RegexOptions.IgnoreCase); 

     // Define a test string.   
     string text = "The the quick brown fox fox jumped over the lazy dog dog."; 

     // Find matches. 
     MatchCollection matches = rx.Matches(text); 

     // Report the number of matches found. 
     Console.WriteLine("{0} matches found in:\n {1}", 
          matches.Count, 
          text); 

     // Report on each match. 
     foreach (Match match in matches) 
     { 
      GroupCollection groups = match.Groups; 
      Console.WriteLine("'{0}' repeated at positions {1} and {2}", 
           groups["word"].Value, 
           groups[0].Index, 
           groups[1].Index); 
     } 

    } 

} 
// The example produces the following output to the console: 
//  3 matches found in: 
//   The the quick brown fox fox jumped over the lazy dog dog. 
//  'The' repeated at positions 0 and 4 
//  'fox' repeated at positions 20 and 25 
//  'dog' repeated at positions 50 and 54 
0

Craig est droit — utilisation des expressions régulières. Regex.Split peut être plus concis pour vos besoins.

0

[^ \ t] + \ t | "[^"] + "\ t

en utilisant l'expression régulière ressemble vraiment à Le meilleur pari, mais celui-ci ne fait que renvoyer toute la chaîne, j'essaie de la peaufiner, mais pas beaucoup de chance jusqu'ici.

string[] tokens = System.Text.RegularExpressions.Regex.Split(this.BuildArgs, @"[^\t]+\t|""[^""]+""\t"); 
+0

Cela ne fonctionnera pas car Regex.Split est conçu pour capturer sur la base de séparateurs, pas de jetons. Utilisez Regex.Match pour obtenir l'effet désiré. –

3

Il existe l'approche machine d'état. Il peut facilement être étendu pour des choses comme des guillemets imbriqués et s'échapper. En retournant IEnumerable<string>, votre code ne peut analyser que ce dont vous avez besoin. Il n'y a pas de réels inconvénients à ce genre d'approche paresseuse, car les chaînes sont immuables, donc vous savez que input ne va pas changer avant d'avoir analysé le tout.

Voir: http://en.wikipedia.org/wiki/Automata-Based_Programming

Questions connexes