2015-05-10 32 views
13

Próbuję oddzielić ciąg na wiele ciągów, aby dostosować terminal. Do tej pory oddzielałem sygnały sterujące za pomocą strtok, jednak nie rozumiem, jak oddzielać określone instancje postaci. Na przykład:Rozdzielanie ciągu znaków w C++

string input = "false || echo \"hello world\" | grep hello";

Podczas próby strtok ten input i próbując oddzielić za pomocą | wyjście byłoby:

false, echo "hello world", grep hello

Zamiast tego chciałbym wyjście do być:

false || echo "hello world", grep hello

W jaki sposób mogę traktować strtok traktować | i || inaczej niż mówiąc, że są one takie same?

+1

"* Jak mogę mieć strtok leczyć? | i || inaczej zamiast go mówiąc, że są takie same *" - Dzieje się tak, ponieważ 'strtok' uważa każdy znak w drugim argumencie jako ogranicznik. Ponadto nie zwraca pustego ciągu. Podobne [1] (http://stackoverflow.com/questions/29847915/implementing-strtok-whose-delimiter-has-more-than-one-character), [2] (http://stackoverflow.com/questions/ 7079694/is-there-a-way-to-split-a-string-on-multiple-characters-in-c? Lq = 1) –

Odpowiedz

8
#include <iostream> 
#include <string> 
#include <algorithm> 
#include <vector> 
using namespace std; 

vector<string> split(string sentence,char delim) 
{ 
    string tempSentence = ""; 
    tempSentence += delim; 
    tempSentence += sentence; 
    tempSentence += delim; 

    string token; 
    vector<string> tokens; 
    for (int i=1;i<tempSentence.length()-1;++i) 
    { 
     if (tempSentence[i] == delim && tempSentence[i-1] != delim && tempSentence[i+1] != delim) 
     { 
      if (token.length()) tokens.push_back(token); 
      token.clear(); 
     } 
     else 
     { 
      token += tempSentence[i]; 
     } 
    } 
    if (token.length()) tokens.push_back(token); 

    return tokens; 
} 

int main() { 
    string sentence = "false || echo \"hello world\" | grep hello"; 
    char delim='|'; 

    vector<string> tokens = split(sentence,delim); 


    for_each(tokens.begin(), tokens.end(), [&](string t) { 
     cout << t << endl; 
    }); 

} 

brzydki i długi! ale działa!

+0

Pytanie, jak byś zmienił kod w przypadku, gdyby użytkownik chciał oddzielić ciąg używając '||' zamiast tego, ponieważ używanie twojego kodu nie działałoby, ponieważ 'char delim' musiałby być po prostu jednym znakiem. Dziękuję również, ponieważ działa idealnie, jeśli szukasz tylko jednego ogranicznika. – divyanshch

+1

Można to łatwo naprawić, po prostu zastąpić ciągiem. Jednak użycie jednego z wielu możliwych ograniczników to funkcja, która nie jest łatwa do dodania. –

1

strtok() będzie skanować znak po znaku, bez względu na znaki przed i po tym, czego szuka. Jeśli chcesz inteligentniejszego skanowania, musisz sam zaimplementować dodatkowe sprawdzanie.

Ponieważ strtok po prostu zwraca lokalizację w ciągu znaków, w którym znajduje się token, musisz ręcznie sprawdzić pierwszy znak zwracanego tokena, aby sprawdzić, czy jest to również znak "|", a następnie podjąć odpowiednie działania.

Lepszym rozwiązaniem byłoby przyjrzenie się używaniu tutaj wyrażenia regularnego. Wygląda na to, że symbol, który chcesz podzielić, to nie tylko |, ale raczej | otoczone spacjami - tzn. faktycznie wyszukujesz i dzielisz na trzyznakowy symbol (spacja - rura - przestrzeń)

1

Powiedziałbym, że odpowiedź na twoje pytanie jest po pierwsze nie używać strtok(), który ma wiele problemów, które są nawet udokumentowane w podręczniku (przynajmniej w systemie Linux).

Po drugie, upewnij się, że masz testy. Korzystanie z rozwoju kierowanego testami jest koniecznością dla tych zadań, ponieważ tutaj kilka prostych rzeczy może wchodzić w interakcje ze sobą źle, a usunięcie błędu w jednym miejscu może powodować problemy w innym.

Ponadto istnieją narzędzia (np. Różne warianty YACC i podobne generatory), które umożliwiają określenie abstrakcyjnej składni, a następnie przekształcenie tej definicji w kod C++. Proponuję je dla każdego nietrywialnego zadania.

Wreszcie, jeśli robisz to tylko dla zabawy i nauki, dobrym pomysłem jest napisanie pętli lub zestawu funkcji do wyodrębniania różnych znaków z łańcucha.

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

using namespace std; 

int main() { 
    string input = "false || echo \"hello world\" | grep hello"; 

    string::iterator itr; 

    itr = input.begin(); 

    do { 
     itr = search_n(itr, input.end(), 1, '|'); 

     if (itr < input.end() - 1) 
     { 
      if (*(itr + 1) == '|') 
      { 
       itr = itr + 2; 
       continue; 
      } 
     }   

     if (itr < input.end()) 
     { 
       *itr = ','; 
       itr ++; 
     } 

    } while (itr < input.end()); 

    cout << input << endl; 

    return 0; 
} 
1

Dość prosty i proste rozwiązanie, które wydaje się rozwiązywać swoje pytanie.

std :: string :: find() przeszukuje ciąg dla pierwszego wystąpienia sekwencji określonej przez jego argumentów (w tym przypadku napis 'separatora'). Po określeniu pozycji, wyszukiwanie zawiera tylko znaki w pozycji lub po pozycji poz.

Edited

#include <iostream> 
    #include <string> 
    int main(int argc, char const *argv[]) 
    { 
     std::string s = "false || echo \"hello world\" | grep hello"; 
     std::string delimiter = "|"; 

     size_t pos = 0, pos1 = 0, flag = 0; 
     std::string token, token1; 
     while ((pos = s.find(delimiter)) != std::string::npos) { 
      pos1 = s.find(delimiter, pos + delimiter.length()); 
      while (pos1 == pos+1){ 
       pos = pos1; 
       pos1 = s.find(delimiter, pos + delimiter.length()); 
       flag = 1; 
      } 
      if (flag) { 
       token = s.substr(0, pos1); 
       std::cout << token << std::endl; 
       if (pos1 > s.length()) 
        exit(0); 
       s.erase(0, pos1 + delimiter.length()); 
      } 
      else{ 
       token = s.substr(0, pos); 
       std::cout << token << std::endl; 
       s.erase(0, pos + delimiter.length()); 
      } 

     } 
     std::cout << s << std::endl; 
     return 0; 
    } 

WYJŚCIE:

false || echo "Hello World"

grep cześć

+0

Ten kod nie działa w przypadku 'echo" cześć świat "| grep cześć | grep world' należy spodziewać się wyjście: 'echo "Hello World"' 'grep hello' ' grep world' raczej jest: 'echo "Hello World" | grep hello' 'grep world' – divyanshch

+0

Spróbuj teraz! Wydaje się być dobre. – mrdoubtful

+0

Możesz spróbować zoptymalizować kod ... – mrdoubtful