2016-07-28 15 views
1

stelle ich mir eine unbekannte Zeichenfolge, die das folgende Format folgt:Wie extrahiere ich eine Zeichenfolge zwischen geschweiften Klammern auf der gleichen Ebene?

Blablabla 
{ 
    "Some Text" 
    2 
    { 
     "Sub Text" 
     99 
    } 
    2 
    { 
     "Sub Text" 
     99 
    } 
} 
Blablabla2 
{ 
    "Some Text" 
    2 
    { 
     "Sub Text" 
     99 
    } 
} 

ich von dieser Zeichenfolge müssen in der Lage zu extrahieren zwischen der ersten Schicht von Trennzeichen jeder Teilkette ({ und }). Also, in diesem Beispiel läuft die unten Funktion:

ExtractStringBetweenDelimitersOnSameLevel(string, "{", "}") 

Sollte folgende Zeichenkette aus der ursprünglichen Zeichenfolge extrahieren und sie dann zurück:

"Some Text" 
    2 
    { 
     "Sub Text" 
     99 
    } 

Das Problem ist, dass es eine kürzere Zeichenfolge zurückgibt aufgrund der zweiten Schicht von Begrenzern.

Hier ist mein Code:

const int Count(
    const std::string& haystack, 
    const std::string& needle, 
    const int starting_index, 
    const int maximum_index) 
{ 
    int total = 0; 
    int offset = starting_index; 

    size_t current_index = std::string::npos; 
    while ((current_index = haystack.find(needle, offset)) != std::string::npos) 
    { 
     if (current_index >= maximum_index) 
     { 
     break; 
     } 

     total++; 
     offset = static_cast<int>(current_index + needle.size()); 
    } 

    return total; 
} 

const size_t FindNthDelimiter(
    const std::string& haystack, 
    const std::string& needle, 
    const int nth) 
{ 
    int total_found = 0; 
    int offset = 0; 

    size_t current_index = std::string::npos; 
    while ((current_index = haystack.find(needle, offset)) != std::string::npos) 
    { 
     total_found++; 
     offset = static_cast<int>(current_index) + 1; 

     if (total_found == nth) 
     { 
     return offset; 
     } 
    } 

    std::cout << "String does not have nth element." << std::endl; 

    return offset; 
} 

std::string ExtractStringBetweenDelimitersOnSameLevel(
    std::string& original_string, 
    const std::string& opening_delimiter, 
    const std::string& closing_delimiter) 
{ 
    // Find the first delimiter... 
    const size_t first_delimiter = original_string.find(opening_delimiter); 
    if (first_delimiter != std::string::npos) 
    { 
     const size_t second_delimiter = original_string.find(closing_delimiter); 
     if (second_delimiter != std::string::npos) 
     { 
     // Total first delimiters found until first closed delimiter... 
     int total_first_delimiters = Count(original_string, opening_delimiter, static_cast<int>(first_delimiter), static_cast<int>(second_delimiter)); 
     const size_t index_of_nth_closer = FindNthDelimiter(original_string, closing_delimiter, total_first_delimiters); 

     std::string needle = original_string.substr(first_delimiter + opening_delimiter.size(), index_of_nth_closer - opening_delimiter.size() - 1); 
     original_string.erase(first_delimiter, index_of_nth_closer + closing_delimiter.size()); 

     return needle; 
     } 
    } 

    return ""; 
} 
+1

Iterieren durch Zeichenfolge, inkrementieren einen Zähler, wenn Sie ein {drücken, dekrementieren Sie einen Zähler, wenn Sie ein} drücken, wenn Ihr Zähler auf 0 zurückgeht, kopieren Sie die Teilzeichenfolge. –

+0

Danke @JonathanPotter Mir fiel es schwer, über einen Weg nachzudenken, das zu tun .. Nicht sicher warum, scheint jetzt einfach, aber haha. – Ricky

Antwort

1

"Je mehr überdenken Sie die Sanitär-, desto leichter ist es ist den Abfluss zu stoppen up" - Scotty, Star Trek III.

Der angezeigte Code scheint für eine so einfache Aufgabe überentwickelt zu sein.

Auch scheint es nicht einmal die angegebene Aufgabe vollständig zu implementieren. Die Aufgabe war, als Extraktions jedes Top-Level-String beschrieben:

jeweils zwischen der ersten Schicht der Trennzeichen in Teilzeichen

Aber die gezeigte Code nur die erste zu extrahieren erscheint. Es lohnt sich nicht herauszufinden, wo dieser komplizierte Algorithmus falsch läuft. Es ist einfacher, es einfach umzuschreiben, um die gesamte Aufgabe zu erledigen, und zwar um die Hälfte der ursprünglichen Größe. Dies sollte nicht mehr als ein Dutzend oder zwei Zeilen Code benötigen, zumindest für den Wurzelalgorithmus. Und der Code, um nur die erste Zeichenkette zu extrahieren, war schon um ein Vielfaches länger.

Im folgenden Beispiel wird jede Zeichenfolge der obersten Ebene zwischen den übereinstimmenden Trennzeichen { und } extrahiert und an einen Lambda-Callback zurückgegeben. main() liefert eine Probe Lambda, die jede Saite druckt std::cout

#include <string> 
#include <algorithm> 
#include <iostream> 

template<typename functor_type> void ExtractStringBetweenDelimitersOnSameLevel(
    const std::string &original_string, 
    char opening_delimiter, // Should be '{' 
    char closing_delimiter, // Should be '}' 
    functor_type &&functor) // Lambda that receives each string. 
{ 
    auto b=original_string.begin(), e=original_string.end(), p=b; 

    int nesting_level=0; 

    while (b != e) 
    { 
     if (*b == closing_delimiter) 
     { 
      if (nesting_level > 0 && --nesting_level == 0) 
      { 
       functor(std::string(p, b)); 
      } 
     } 

     if (*b++ == opening_delimiter) 
     { 
      if (nesting_level++ == 0) 
       p=b; 
     } 
    } 
} 


int main() 
{ 
    std::string search_string="\n" 
     "Blablabla\n" 
     "{\n" 
     " \"Some Text\"\n" 
     " 2\n" 
     " {\n" 
     "  \"Sub Text\"\n" 
     "   99\n" 
     " }\n" 
     " 2\n" 
     " {\n" 
     "  \"Sub Text\"\n" 
     "   99\n" 
     " }\n" 
     "}\n" 
     "Blablabla2\n" 
     "{\n" 
     " \"Some Text\"n" 
     " 2\n" 
     " {\n" 
     "  \"Sub Text\"\n" 
     "   99\n" 
     " }\n" 
     "}"; 

    ExtractStringBetweenDelimitersOnSameLevel 
     (search_string, 
     '{', 
     '}', 
     [](const std::string &string) 
     { 
      std::cout << "Extracted: " << string << std::endl; 
     }); 
} 

Ihre Hausaufgabe ist diese Multi-Zeichenbegrenzern zu handhaben zu ändern. Dies sollte auch nicht viel komplizierter sein.

+0

Es ist keine Hausaufgabe, ich versuche, ein Programm zu schreiben, Konfigurationsdatei für das Programm selbst zu analysieren (die Konfiguration ist ziemlich kompliziert). Außerdem müssen die Trennzeichen Strings sein (oder char *) ... Was ist ein Funktor? Ich habe eine harte Art, die effiziente Suchmethoden ergreift, obwohl ich sehen kann, wie das Verfolgen des Nestniveaus hier funktionieren würde. In jedem Fall glaube ich, dass ich die allgemeine Vorstellung davon hatte, was Sie hier versuchen wollten. – Ricky

+0

Funktor: a.k.a.Funktionsobjekt, alias Lambda.Das Funktionsobjekt, das hier 'main()' an die Template-Funktion übergibt, die die extrahierte Zeichenkette nach 'std :: cout' druckt und die Template-Funktion für jede extrahierte Zeichenkette aufruft. –