2016-01-05 10 views
7

Jeśli mam string jak tenAnalizować ciąg ze spacjami i cudzysłowem (z cudzysłowem zachowane)

create myclass "56, 'for the better or worse', 54.781" 

Jak mogę analizować to takie, że wynik byłby trzy string „słowa”, które mają następujące zawartość:

[0] create 
[1] myclass 
[2] "56, 'for the better or worse', 54.781" 

Edit 2: zauważ, że cudzysłowy mają być zachowane

Początkowo ja próbowałem za pomocą string.Split(' '), ale zauważyłem, że spowodowałoby to, że trzeci string zostałby uszkodzony na kilka innych ciągów.

Próbuję ograniczyć wynik Split, używając argumentu count jako 3, aby rozwiązać ten problem. I czy to jest w porządku dla tego przypadku, ale gdy podany łańcuch jest

Następnie podział nie powiedzie się, ponieważ dwa ostatnie słowa zostaną połączone.

Stworzyłem też coś podobnego ReadInBetweenSameDepth uzyskać string w między cudzysłowem

Oto moja ReadInBetweenSameDepth metoda

//Examples: 
    //[1] (2 + 1) * (5 + 6) will return 2 + 1 
    //[2] (2 * (5 + 6) + 1) will return 2 * (5 + 6) + 1 
public static string ReadInBetweenSameDepth(string str, char delimiterStart, char delimiterEnd) { 
    if (delimiterStart == delimiterEnd || string.IsNullOrWhiteSpace(str) || str.Length <= 2) 
    return null; 
    int delimiterStartFound = 0; 
    int delimiterEndFound = 0; 
    int posStart = -1; 
    for (int i = 0; i < str.Length; ++i) { 
    if (str[i] == delimiterStart) { 
     if (i >= str.Length - 2) //delimiter start is found in any of the last two characters 
     return null; //it means, there isn't anything in between the two 
     if (delimiterStartFound == 0) //first time 
     posStart = i + 1; //assign the starting position only the first time... 
     delimiterStartFound++; //increase the number of delimiter start count to get the same depth 
    } 
    if (str[i] == delimiterEnd) { 
     delimiterEndFound++; 
     if (delimiterStartFound == delimiterEndFound && i - posStart > 0) 
     return str.Substring(posStart, i - posStart); //only successful if both delimiters are found in the same depth 
    } 
    } 
    return null; 
} 

Ale chociaż ta funkcja działa, uważam, że to bardzo trudno połączyć wynik z string.Split, aby dokonać poprawnego analizowania, jak chcę.

Edit 2: W mojej biednej rozwiązania, muszę ponownie dodać cudzysłów później

Czy istnieje lepszy sposób to zrobić? Jeśli używamy Regex, w jaki sposób to robimy?

Edit:

Szczerze jestem świadoma, że ​​ten problem może być rozwiązany w ten sam sposób jak CSV sformatowanego tekstu. Nie wiedziałem też, że ten problem nie jest koniecznie rozwiązany przez Regex (dlatego nazwałem go jako taki). Moje szczere przeprosiny dla tych, którzy widzą to jako duplikat postu.

Edit 2:

Po pracy więcej na temat mojego projektu, zdałem sobie sprawę, że coś było nie tak z moim pytaniem (czyli nie obejmują cudzysłów) - Moje przeprosiny do poprzednio najlepiej odpowiadającego, Mr Tim Schmelter. A potem, patrząc na dupe-link, zauważyłem, że nie zapewnia to również odpowiedzi.

Odpowiedz

2

Regex Demo

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

Get mecze w pierwszej grupie przechwytywania.

  1. \w+: Zapałki znaki alfanumeryczne i podkreślenia jednej lub więcej razy
  2. "[^"]*": Zapałki niczego, co jest owinięty w cudzysłowach
  3. |: lub stan w regex
+0

Dzięki, przetestowałem 'Regex' i to działało dobrze. Doceń również wyjaśnienie. – Ian

+0

dziękuję panu Tusharowi, po dalszej pracy z danymi, wydaje się, że twoje jest najlepszym rozwiązaniem, z wyjaśnieniami. Oto mój drugi post, który sprawia, że ​​tak myślę: http://stackoverflow.com/questions/34624536/stringsplitoptions-removeemptyentries-equivalent-for-textfieldparser – Ian

3

Można podzielić przez ten

\s(?=(?:[^"]*"[^"]*")*[^"]*$) 

Zobacz demo.

https://regex101.com/r/fM9lY3/60

string strRegex = @"\s(?=(?:[^""]*""[^""]*"")*[^""]*$)"; 
Regex myRegex = new Regex(strRegex, RegexOptions.Multiline); 
string strTargetString = @"create myclass ""56, 'for the better or worse', 54.781"""; 

return myRegex.Split(strTargetString); 
+0

Dzięki, myślę, że jest to najlepsza odpowiedź, ponieważ używam C# do zadania. Szczerze mówiąc, nie jestem świadomy, że mój problem jest taki sam jak analiza CSV. – Ian

+1

Och, daj spokój, to najgorsza odpowiedź tutaj! ** Nie używaj tego wyrażenia regularnego, jeśli możesz obejść się bez niego ** Sprawdź, ile obejmuje to wycofywanie. Wolałbym wybrać odpowiedź z większą ilością wyjaśnień. –

+0

@stribizhev czy masz lepszą odpowiedź, sir? Ponieważ moja własna metoda jest oczywiście gorsza niż wszystkie podane odpowiedzi. – Ian

1

użyłbym prawdziwa csv-parser dla tego zadania. Jedynym, dostępny w ramach jest TextFieldParser-class w przestrzeni nazw VisualBasic:

string str = "create myclass \"56, 'for the better or worse', 54.781\""; 
var allLineFields = new List<string[]>(); 
using (var parser = new Microsoft.VisualBasic.FileIO.TextFieldParser(new StringReader(str))) 
{ 
    parser.Delimiters = new string[] { " " }; 
    parser.HasFieldsEnclosedInQuotes = true; // important 
    string[] lineFields; 
    while ((lineFields = parser.ReadFields()) != null) 
    { 
     allLineFields.Add(lineFields); 
    } 
} 

Wynik:

enter image description here

Ale istnieją inne dostępne jak this lub this.

+0

Dzięki, nie wiedziałem, że istnieje "TextFieldParser" w bibliotece VB, która może użyj tego w ten sposób. Doceniam twój wkład. +10 – Ian

+0

@Ian: Możesz go używać z C# bez problemu. Jest bardziej wydajny niż użycie wyrażenia regularnego, jeśli faktycznie analizujesz większy tekst. Nie ma za co. –

+0

O, widzę ... moje złe. Oczywiście, ponieważ jest on konwertowany na 'dll', to powinien być raczej' .Net' niż 'VB', który może być dość łatwo użyty w C#. Dziękuję za poprawienie mnie. Równie dobrze widziałbym jego wydajność. – Ian

Powiązane problemy