2008-09-10 31 views
7

BuAlan Ayrılmış Metin ayrıştırmak için en iyi yol

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

gibi bir dizgim var. Bunu belirtmek istiyorum ancak boşluklara ayıramam. Çalıştığım biraz çirkin çözümleyici ile geldim, ama ben kimsenin daha şık bir tasarıma sahip olup olmadığını merak ediyorum.

Bu C# btw içinde.

DÜZENLEME: Çirkin versiyonum çirkinken, O (N) ve aslında bir RegEx kullanmaktan daha hızlı olabilir.

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

bize sen boşluklar üzerinde bölemezsiniz neden dahil başarmaya çalıştığını şeyle ilgili daha fazla bilgi verin. Ardından cevaplarımızı durumunuza göre düzenleyebiliriz. –

cevap

16

lexical analysis ne yaptığınızı için bilgisayar terimi; Bu ortak görevin iyi bir özeti için okuyun. senin örnekten yola çıkarak

, sana boşluk sözlerinizi ayırmak istediğinizi tahmin ediyorum, ama tırnak içinde malzeme tırnak işaretleri olmadan bir "kelime" olarak ele alınmalıdır.

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

Bu ifade, bir "kelime" (1) olmayan alıntı boşluk olmayan metin çevrili ya olduğunu söyler:

Bunu yapmanın en kolay yolu, normal ifade olarak bir kelime tanımlamaktır beyaz boşluk veya (2) tırnak işareti ile çevrelenen alıntı dışı metin (bazı boşluklarla takip edilir). İstenen metni vurgulamak için parantezin yakalanmasının kullanıldığına dikkat edin. Bu regex ile

Silahlı senin algoritması basit: yakalayan parantez tarafından tanımlandığı şekilde bir sonraki "söz" için metin arayabilir ve geri. "Sözler" inizi bitene kadar tekrarlayın.

Burada VB.NET içinde, ben ile gelebilir çalışan kod en basit kısmı. İki yakalama parantezi kümesi bulunduğundan, veriler için her iki grubunu kontrol etmemiz gerektiğini unutmayın.

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 

Not 1: Will's cevap, yukarıda, bu aynı fikirdir. Umarım bu cevabı sahnesi biraz daha iyi :)

8

Microsoft.VisualBasic.FileIO ad uzay delimeted metin üzerinde bölmek için kullanabileceğiniz bir TextFieldParser vardır. Dizeleri tırnak içine alır (yani "bu bir token" thisistokentwo).

Not

, DLL diyor sırf VisualBasic yalnızca VB projesinde kullanmak anlamına gelmez. Bütün Çerçevenin bir parçası.

0

Ayrıca düzenli ifadelere bakmak isteyebilirsiniz. Bu size yardımcı olabilir. Aşağıda örnek bir

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 sağ — kullanımı normal ifadeler olduğunu ... MSDN'den sökük edilir. Regex.Split ihtiyaçlarınız için daha kısa olabilir.

0

[^ \ t] + \ t arkasında ayrıntıları açıklıyor | "[^"] + "\ t

Regex kesinlikle benziyor kullanarak en iyi bahis, ancak bu sadece tüm dizgeyi döndürür.Bunu düzeltmeye çalışıyorum ama şimdiye kadar pek şansım yok.

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

Bu çalışma çalışmayacaktır çünkü Regex.Split, belirteçlere değil, belirteçlere göre yakalanacak şekilde tasarlanmıştır. İstenen efekti elde etmek için Regex.Match kullanın. –

3

Durum makinesi yaklaşımı var.

private enum State 
    { 
     None = 0, 
     InTokin, 
     InQuote 
    } 

    private static IEnumerable<string> Tokinize(string input) 
    { 
     input += ' '; // ensure we end on whitespace 
     State state = State.None; 
     State? next = null; // setting the next state implies that we have found a tokin 
     StringBuilder sb = new StringBuilder(); 
     foreach (char c in input) 
     { 
      switch (state) 
      { 
       default: 
       case State.None: 
        if (char.IsWhiteSpace(c)) 
         continue; 
        else if (c == '"') 
        { 
         state = State.InQuote; 
         continue; 
        } 
        else 
         state = State.InTokin; 
        break; 
       case State.InTokin: 
        if (char.IsWhiteSpace(c)) 
         next = State.None; 
        else if (c == '"') 
         next = State.InQuote; 
        break; 
       case State.InQuote: 
        if (c == '"') 
         next = State.None; 
        break; 
      } 
      if (next.HasValue) 
      { 
       yield return sb.ToString(); 
       sb = new StringBuilder(); 
       state = next.Value; 
       next = null; 
      } 
      else 
       sb.Append(c); 
     } 
    } 

Yuvalanmış tırnaklar ve kaçış gibi şeyler için kolayca genişletilebilir. IEnumerable<string> olarak geri dönmeniz, kodunuzun yalnızca ihtiyaç duyduğunuz kadar ayrıştırmasına olanak tanır. Dizeler değişmez olduğundan bu tür bir tembel yaklaşım için gerçek bir dezavantaj yok, bu yüzden her şeyi ayrıştırmadan önce input'un değişmeyeceğini biliyorsunuz.

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

İlgili konular