2015-05-10 23 views
13

Sto provando a separare una stringa in più stringhe, per creare un terminale personalizzato. Finora ho separato i segnali di controllo usando strtok, tuttavia non capisco come separare specifiche istanze di un personaggio. Per esempio:Separazione di una stringa in C++

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

Quando si cerca di strtok questo input e cercando di separare utilizzando | l'output sarà:

false, echo "hello world", grep hello

Invece, vorrei l'output be:

false || echo "hello world", grep hello

Come è possibile che strtok tratti | e || invece di dire che sono uguali?

+1

"* Come posso avere strtok trattare |? e || in modo diverso, piuttosto che averlo dicendo che sono la stessa *" - Questo accade perché 'strtok' considera ogni personaggio nel secondo argomento è un delimitazione. Inoltre, non restituisce una stringa vuota. Relativo [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-su-multiplo-caratteri-in-c? Lq = 1) –

risposta

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; 
    }); 

} 

brutto e lungo! ma funziona!

+0

Domanda come cambieresti il ​​codice nel caso in cui l'utente volesse separare la stringa usando '||' invece perché usando il tuo codice non funzionerebbe dato che 'char delim' dovrebbe essere un solo carattere. Anche grazie perché funziona perfettamente se stai cercando un solo delim. – divyanshch

+1

Questo è facilmente risolvibile, basta sostituirlo con una stringa. Tuttavia, l'utilizzo di uno dei molteplici delimitatori possibili è una funzionalità non facile da aggiungere. –

1

strtok() esegue la scansione carattere per carattere, indipendentemente dai caratteri prima e dopo quello che sta cercando. Se si desidera una scansione più intelligente, è necessario implementare il controllo aggiuntivo autonomamente.

Poiché strtok restituisce solo una posizione all'interno della stringa in cui viene trovato un token, è necessario verificare manualmente il primo carattere del token restituito per vedere se è anche un '|', quindi agire di conseguenza.

Una soluzione migliore sarebbe esaminare l'uso di un'espressione regolare qui. Sembra che il simbolo su cui vuoi dividere non sia solo un |, ma piuttosto un | circondato da spazi - cioè, in realtà stai cercando e dividendo su un simbolo di tre caratteri (spazio - tubo - spazio)

1

Direi che la risposta alla tua domanda è in primo luogo di non usare strtok(), che ha una moltitudine di problemi, che sono anche documentati nella manpage (almeno su Linux).

In secondo luogo, assicurarsi di disporre di test. L'utilizzo dello sviluppo test-driven è un must per queste attività, perché qui diverse cose semplici potrebbero interagire male tra loro e correggere un bug in un posto può causare problemi in un altro.

Inoltre, esistono strumenti (ad esempio varie varianti YACC e generatori simili) che consentono di specificare una sintassi astratta e quindi di trasformare questa definizione in codice C++. Suggerirei questi per qualsiasi compito non banale.

Infine, se si sta solo facendo questo per divertimento e apprendimento, scrivere un ciclo o un insieme di funzioni per estrarre vari token da una stringa è un buon approccio.

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

Una soluzione abbastanza semplice e lineare che sembra risolvere la tua domanda.

Lo std :: string :: find() Cerca la stringa per la prima occorrenza della sequenza specificata da suoi argomenti (in questo caso la stringa 'delimitatore'). Quando viene specificato pos, la ricerca include solo caratteri in corrispondenza o dopo posizione pos.

cura

#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; 
    } 

USCITA:

falsa || echo "Ciao mondo"

grep ciao

+0

Questo codice non funziona nel caso di 'echo" ciao mondo "| ciao ciao | grep world' l'uscita dovrebbe essere prevista per: 'echo "ciao mondo"' ' grep hello' ' grep world' piuttosto è: 'echo "Ciao mondo" | grep hello' 'grep world' – divyanshch

+0

Prova ora! Sembra essere buono. – mrdoubtful

+0

Si potrebbe provare a ottimizzare il codice anche se ... – mrdoubtful