2012-04-07 28 views
20

Sé que esto es un problema bastante fácil, pero sólo quiero resolverlo por mí mismo una vez por todasSeparar una cadena por un carácter

me gustaría simplemente dividir una cadena en una matriz mediante un personaje como el delimitador dividido (Al igual que la famosa .split() función de la C# 's. Por supuesto, puedo aplicar el enfoque de fuerza bruta pero me pregunto si hay algo mejor que eso.

Hasta ahora la he buscado y probablemente el más cercano enfoque de solución es el uso de strtok(), sin embargo, debido a que es inconveniente (la conversión de su cadena a una matriz de caracteres, etc.) que no me gusta usarlo. ¿hay alguna manera más fácil de implementar esto?

Nota: Quería enfatizar esto porque la gente podría preguntar "¿Cómo es que la fuerza bruta no funciona?". Mi solución de fuerza bruta fue crear un ciclo, y usar el substr() función dentro. Sin embargo, dado que requiere el punto de partida y la longitud, falla cuando quiero dividir una fecha. Debido a que el usuario puede ingresarlo como 7/12/2012 o 03/07/2011, donde realmente puedo decir la longitud antes de calcular la siguiente ubicación del delimitador '/'.

+0

posible duplicado de [Splitting String C++] (http://stackoverflow.com/questions/275404/splitting-string-c) –

Respuesta

49

Uso de vectores, cadenas y cadenas de caracteres. Un poco engorroso, pero funciona.

std::stringstream test("this_is_a_test_string"); 
std::string segment; 
std::vector<std::string> seglist; 

while(std::getline(test, segment, '_')) 
{ 
    seglist.push_back(segment); 
} 
+0

En realidad, este tipo de enfoque es exactamente lo que estoy buscando. Muy fácil de entender, sin uso de librerías externas, solo muy directo. Gracias @thelazydeveloper! – Ali

+0

¡Sigue siendo la mejor solución disponible! –

2

Tome un vistazo a boost::tokenizer

Si desea enrollar su propio método, se puede utilizar std::string::find() para determinar los puntos de división.

+2

Gracias por el consejo de búsqueda de cuerdas. Siempre me encanta escuchar soluciones ** std **! – Ali

9

Boost tiene la split() que está buscando en algorithm/string.hpp:

std::string sample = "07/3/2011"; 
std::vector<string> strs; 
boost::split(strs, sample, boost::is_any_of("/")); 
10

Otra forma (C++ 11/impulso) para las personas que les gusta RegEx. Personalmente, soy un gran admirador de RegEx para este tipo de datos. IMO es mucho más poderoso que simplemente dividir cadenas usando un delimitador ya que puedes elegir ser mucho más inteligente sobre lo que constituye datos "válidos" si lo deseas.

#include <string> 
#include <algorithm> // copy 
#include <iterator>  // back_inserter 
#include <regex>  // regex, sregex_token_iterator 
#include <vector> 

int main() 
{ 
    std::string str = "08/04/2012"; 
    std::vector<std::string> tokens; 
    std::regex re("\\d+"); 

    //start/end points of tokens in str 
    std::sregex_token_iterator 
     begin(str.begin(), str.end(), re), 
     end; 

    std::copy(begin, end, std::back_inserter(tokens)); 
} 
4

Otra posibilidad es imbuir a un arroyo con una configuración regional que utiliza un ctype faceta especial. Una secuencia usa la faceta ctype para determinar qué es "espacio en blanco", que trata como separadores. Con una faceta de tipo c que clasifica tu carácter separador como espacios en blanco, la lectura puede ser bastante trivial.He aquí una manera de implementar la faceta:

struct field_reader: std::ctype<char> { 

    field_reader(): std::ctype<char>(get_table()) {} 

    static std::ctype_base::mask const* get_table() { 
     static std::vector<std::ctype_base::mask> 
      rc(table_size, std::ctype_base::mask()); 

     // we'll assume dates are either a/b/c or a-b-c: 
     rc['/'] = std::ctype_base::space; 
     rc['-'] = std::ctype_base::space; 
     return &rc[0]; 
    } 
}; 

Utilizamos que mediante el uso imbue para contar una secuencia a utilizar un entorno local que lo incluye, a continuación, leer los datos de esa corriente:

std::istringstream in("07/3/2011"); 
in.imbue(std::locale(std::locale(), new field_reader); 

Con eso en su lugar, la división se convierte en casi trivial - sólo inicializar un vector usando un par de istream_iterator s para leer las piezas de la cadena (que se incrusta en el istringstream):

std::vector<std::string>((std::istream_iterator<std::string>(in), 
          std::istream_iterator<std::string>()); 

Obviamente, esto tiende a exagerar si solo lo usa en un solo lugar. Sin embargo, si lo usas mucho, puede recorrer un largo camino para mantener el resto del código bastante limpio.

0

I inherentemente no me gusta stringstream, aunque no estoy seguro de por qué. Hoy, escribí esta función para permitir dividir un std::string por cualquier carácter o cadena arbitraria en un vector. Sé que esta pregunta es antigua, pero quería compartir una forma alternativa de dividir std::string.

Este código omite por completo la parte de la cadena que separa de los resultados, aunque podría modificarse fácilmente para incluirlos.

#include <string> 
#include <vector> 

void split(std::string str, std::string splitBy, std::vector<std::string>& tokens) 
{ 
    /* Store the original string in the array, so we can loop the rest 
    * of the algorithm. */ 
    tokens.push_back(str); 

    // Store the split index in a 'size_t' (unsigned integer) type. 
    size_t splitAt; 
    // Store the size of what we're splicing out. 
    size_t splitLen = splitBy.size(); 
    // Create a string for temporarily storing the fragment we're processing. 
    std::string frag; 
    // Loop infinitely - break is internal. 
    while(true) 
    { 
     /* Store the last string in the vector, which is the only logical 
     * candidate for processing. */ 
     frag = tokens.back(); 
     /* The index where the split is. */ 
     splitAt = frag.find(splitBy); 
     // If we didn't find a new split point... 
     if(splitAt == string::npos) 
     { 
      // Break the loop and (implicitly) return. 
      break; 
     } 
     /* Put everything from the left side of the split where the string 
     * being processed used to be. */ 
     tokens.back() = frag.substr(0, splitAt); 
     /* Push everything from the right side of the split to the next empty 
     * index in the vector. */ 
     tokens.push_back(frag.substr(splitAt+splitLen, frag.size()-(splitAt+splitLen))); 
    } 
} 

de usar, basta llamar así ...

std::string foo = "This is some string I want to split by spaces."; 
std::vector<std::string> results; 
split(foo, " ", results); 

Ahora puede acceder a todos los resultados en el vector a voluntad. Tan simple como eso, no stringstream, no hay bibliotecas de terceros, ¡no hay que volver a C!

Cuestiones relacionadas