2009-05-20 23 views
8

Soy nuevo en la programación. He estado tratando de escribir una función en C++ que explota el contenido de una cadena en una matriz de cadenas a un parámetro dado, ejemplo:¿Cómo puedo dividir una cadena por un delimitador en una matriz?

string str = "___this_ is__ th_e str__ing we__ will use__"; 

deben regresar matriz de cadenas:

cout << stringArray[0]; // 'this' 
cout << stringArray[1]; // ' is' 
cout << stringArray[2]; // ' th' 
cout << stringArray[3]; // 'e str' 
cout << stringArray[4]; // 'ing we' 
cout << stringArray[5]; // ' will use' 

puedo tokenize la cadena está bien, pero la parte más difícil para mí es cómo puedo especificar el número de elementos en stringArray antes de asignarle el toke de la cadena actual y también cómo devolver stringArray desde la función.

¿Alguien me mostraría cómo escribir la función?

Edit1: no necesariamente tienen los resultados al estado en matriz de cadenas simplemente cualquier recipiente que pueda llamar como una variable regular con algún tipo de indexación.

+0

tarea, por ventura? Eso está bien, por supuesto, pero estoy de las preguntas de la tarea de respuesta de manera diferente multitud ... – dmckee

+0

duplicado: http://stackoverflow.com/questions/53849/how-do-i-tokenize-a-string-in-c – lothar

+0

@Iothar responde aquí parece ser más eficiente. – Arnthor

Respuesta

10

Aquí es mi primer intento de esto utilizando vectores y cadenas:

vector<string> explode(const string& str, const char& ch) { 
    string next; 
    vector<string> result; 

    // For each character in the string 
    for (string::const_iterator it = str.begin(); it != str.end(); it++) { 
     // If we've hit the terminal character 
     if (*it == ch) { 
      // If we have some characters accumulated 
      if (!next.empty()) { 
       // Add them to the result vector 
       result.push_back(next); 
       next.clear(); 
      } 
     } else { 
      // Accumulate the next character into the sequence 
      next += *it; 
     } 
    } 
    if (!next.empty()) 
     result.push_back(next); 
    return result; 
} 

Esperemos que esto le da algún tipo de idea de cómo hacer esto.En su ejemplo de cadena que devuelve los resultados correctos con este código de prueba:

int main (int, char const **) { 
    std::string blah = "___this_ is__ th_e str__ing we__ will use__"; 
    std::vector<std::string> result = explode(blah, '_'); 

    for (size_t i = 0; i < result.size(); i++) { 
     cout << "\"" << result[i] << "\"" << endl; 
    } 
    return 0; 
} 
+1

El primer parámetro para explotar() debe ser una referencia constante. El compilador se quejará entonces 'it' debe ser una cadena :: const_iterator. – ralphtheninja

+0

@Magnus: Gracias, agregué ese arreglo :) –

+1

Debería verificar si 'next' no está vacío y si es así, añádalo a los resultados DESPUÉS DEL LAZO. De lo contrario, el último elemento después del char de explosión no se incluirá. – ddinchev

1

Si insiste en hacer stringArray una matriz como oppossed a un std::vector<> (que sería lo que hay que hacer) lo que tiene que o bien:

  1. Hacer dos pases (uno para contar, que se ve)
  2. Implemente una matriz dinámica usted mismo.

Usar un vector es más fácil vector::push_back() agrega cosas nuevas al final. Por lo tanto:

vector* explode(string s){ 
    vector<string> *v = new vector<string> 
    //... 
    // in a loop 
    v->push_back(string_fragment); 
    //... 
    return v; 
} 

no es necesario después de todo dejar adentro por completo.

Para devolver la matriz de cadenas que usa char **.

Como en

char ** explode(const char *in){ 
    ... 

} 

BTW-- Cómo será la función de llamada saben cuántos elementos hay en la matriz devuelta? Tendrás que resolver eso también. Use std::vector<> a menos que esté limitado por fuerzas externas ...

+0

v-> push_back (string_fragment); – ralphtheninja

+0

@Magnus: Ups. Gracias. – dmckee

+0

Creo que pasar una pila de vector asignado de regreso, o tomar una referencia a un vector para llenar sería una mejor opción que asignar un vector sin procesar en la función, y luego pasar la responsabilidad de eliminar al cliente. – GManNickG

1

Puede usar un vector de cadena (std::vector<std::string>), anexarle cada token con push_back y luego devolverlo desde la función tokenize.

1

Utilice std :: vector como una matriz dinámica y devuélvala como resultado.

1

Quizás debería usar una lista en lugar de una matriz. De esa forma no necesitarías saber la cantidad de elementos antes de tiempo. También puede considerar usar los contenedores STL.

8

Usando STL (lo siento no compilador no probado)

#include <vector> 
#include <string> 
#include <sstream> 

int main() 
{ 
    std::vector<std::string> result; 

    std::string str = "___this_ is__ th_e str__ing we__ will use__"; 

    std::stringstream data(str); 

    std::string line; 
    while(std::getline(data,line,'_')) 
    { 
     result.push_back(line); // Note: You may get a couple of blank lines 
           // When multiple underscores are beside each other. 
    } 
} 

// o definir un símbolo de

#include <vector> 
#include <string> 
#include <iterator> 
#include <algorithm> 
#include <sstream> 

struct Token: public std::string // Yes I know this is nasty. 
{         // But it is just to demosntrate the principle.  
}; 

std::istream& operator>>(std::istream& s,Token& t) 
{ 
    std::getline(s,t,'_'); 

    // *** 
    // Remove extra '_' characters from the stream. 
    char c; 
    while(s && ((c = s.get()) != '_')) {/*Do Nothing*/} 
    if (s) 
    { 
     s.unget(); // Put back the last char as it is not '_' 
    } 
    return s; 
} 

int main() 
{ 
    std::vector<std::string> result; 

    std::string str = "___this_ is__ th_e str__ing we__ will use__"; 

    std::stringstream data(str); 

    std::copy(std::istream_iterator<Token>(data), 
       std::istream_iterator<Token>() 
       std::back_inserter(result) 
      ); 
} 
+0

Así que agregue la verificación dentro del ciclo while para omitir las "líneas" vacías. – jmucchiello

+1

Eso es un ejercicio para el usuario. –

0

Espere hasta la clase de estructuras de datos y luego codifíquela con una lista vinculada. Sin embargo, si es para hacer los deberes, es posible que pueda salirse con la suya simplemente con ingresar la matriz.

0

El código de abajo:

template <typename OutputIterator> 
int explode(const string &s, const char c, OutputIterator output) { 
    stringstream data(s); 
    string line; 
    int i=0; 
    while(std::getline(data,line,c)) { *output++ = line; i++; } 
    return i; 
} 

int main(...) { 
    string test="H:AMBV4:2:182.45:182.45:182.45:182.45:182.41:32:17700:3229365:201008121711:0"; 
    cout << test << endl; 
    vector<string> event; 
**This is the main call** 
    int evts = explode(test,':', back_inserter(event)); 
    for (int k=0; k<evts; k++) 
    cout << event[k] << "~"; 
    cout << endl; 
} 

salidas

H:AMBV4:2:182.45:182.45:182.45:182.45:182.41:32:17700:3229365:201008121711:0 
H~AMBV4~2~182.45~182.45~182.45~182.45~182.41~32~17700~3229365~201008121711~0~ 
2

Funciona para mí:

#include <iostream> 
#include <vector> 
#include <string> 

using namespace std; 

vector<string> explode(const string &delimiter, const string &explodeme); 

int main(int argc, char *argv[]) 
{ 
    string str = "I have a lovely bunch of cocoa nuts"; 
    cout<<str<<endl; 
    vector<string> v = explode(" ", str); 
    for(int i=0; i<v.size(); i++) 
     cout <<i << " ["<< v[i] <<"] " <<endl; 
} 

vector<string> explode(const string &delimiter, const string &str) 
{ 
    vector<string> arr; 

    int strleng = str.length(); 
    int delleng = delimiter.length(); 
    if (delleng==0) 
     return arr;//no change 

    int i=0; 
    int k=0; 
    while(i<strleng) 
    { 
     int j=0; 
     while (i+j<strleng && j<delleng && str[i+j]==delimiter[j]) 
      j++; 
     if (j==delleng)//found delimiter 
     { 
      arr.push_back( str.substr(k, i-k)); 
      i+=delleng; 
      k=i; 
     } 
     else 
     { 
      i++; 
     } 
    } 
    arr.push_back( str.substr(k, i-k)); 
    return arr; 
} 

fuente:http://www.zedwood.com/article/106/cpp-explode-function

0

Aquí está mi código cocinado (completo). Puede ser útil para algunos con la misma necesidad.

#include <string> 
#include <iostream> 
#include <sstream> 
#include <vector> 
using namespace std; 

int main(){ 
     std::string s = "scott:tiger:mushroom"; 
     std::string delimiter = ":"; 

     std::vector<std::string> outputArr; 
     size_t pos = 0; 
     std::string token; 
     while ((pos = s.find(delimiter)) != std::string::npos) { 
      token = s.substr(0, pos); 
      s.erase(0, pos + delimiter.length()); 
      outputArr.push_back(token); 
     } 
     outputArr.push_back(s); 

     // Printing Array to see the results 
     std::cout<<"====================================================================================\n"; 
     for (int i=0;i<outputArr.size();i++){ 
       std::cout<<outputArr[i]<<"\n"; 
     } 
     std::cout<<"====================================================================================\n"; 
} 

¡Salud!

0

Creo que he escrito una solución mucho más simple.

std::vector<std::string> explode(const std::string& string, const char delimiter) { 

std::vector<std::string> result; 
unsigned int start = 0, pos = 0; 

while (pos != string.length()) { 
    if (string.at(pos) == delimiter || pos + 1 == string.length()) { 
     unsigned int size = (pos - start) + ((pos + 1) == string.length() ? 1 : 0); 
     if (size != 0) { // Make this 'if' as a option? like a parameter with removeEmptyString? 
      result.push_back(string.substr(start, size)); 
     } 
     start = pos + 1; 
    } 
    pos++; 
} 

return std::move(result); 

}

0

Esto funcionó para mí:

#include <iostream> 
#include <vector> 
#include <string> 
#include <sstream> 

using namespace std; 

vector<string> split(string str, char delimiter) { 
    vector<string> internal; 
    stringstream ss(str); // Turn the string into a stream. 
    string tok; 

    while(getline(ss, tok, delimiter)) { 
    internal.push_back(tok); 
    } 

    return internal; 
} 

int main(int argc, char **argv) { 
    string myCSV = "one,two,three,four"; 
    vector<string> sep = split(myCSV, ','); 

    // If using C++11 (which I recommend) 
    /* for(string t : sep) 
    * cout << t << endl; 
    */ 

    for(int i = 0; i < sep.size(); ++i) 
    cout << sep[i] << endl; 
} 

Fuente: http://code.runnable.com/VHb0hWMZp-ws1gAr/splitting-a-string-into-a-vector-for-c%2B%2B

Cuestiones relacionadas