Sé que este es un problema bastante fácil, pero solo quiero resolverlo por mí mismo de una vez por todas.
Simplemente me gustaría dividir una cadena en una matriz usando un carácter como delimitador de división. (Muy parecido al famoso C# .Separar() función. Por supuesto, puedo aplicar el enfoque de fuerza bruta, pero me pregunto si hay algo mejor que eso.
Hasta ahora he buscado y probablemente el más cercano enfoque de solución es el uso de strtok(), sin embargo, debido a su inconveniente (convertir su cadena en una matriz de caracteres, etc.) 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 bucle y usar el substr() función en el interior. Sin embargo, dado que requiere la punto de partida y la longitud, falla cuando quiero dividir una fecha. Porque el usuario puede ingresarlo como 12/7/2012 o 3/07/2011, donde realmente puedo saber la longitud antes de calcular la siguiente ubicación del delimitador “https://stackoverflow.com/”.
posible duplicado de Splitting String C++
– Bo Person
8 de abril de 2012 a las 6:56
¿Responde esto a tu pregunta? ¿Cómo itero sobre las palabras de una cadena?
– xskxzr
28/07/2021 a las 10:00
desarrolladorperezoso
Uso de vectores, cadenas y stringstream. Un poco engorroso pero hace el truco.
En realidad, este tipo de enfoque es exactamente lo que estoy buscando. Bastante fácil de entender, sin uso de bibliotecas externas, simplemente muy sencillo. ¡Gracias @thelazydeveloper!
– Alí
7 abr 2012 a las 22:09
Si desea mejorar el rendimiento, puede agregar seglist.reserve(std::count_if(str.begin(), str.end(), [&](char c) { return c == splitChar; }) + (str.empty() ? 1 : 0)); Si la cadena original para dividir se almacena en str.
– Jarek C.
23 de mayo de 2018 a las 8:10
En lugar de while (std::getline(test, segment, '_')) podría ser mejor hacer while (!std::getline(test, segment, '_').eof()).
– Makonede
15 de enero a las 16:14
chrisaycock
Impulso tiene la separar() estas buscando en algorithm/string.hpp:
Otra forma (C++11/boost) para las personas a las que les gusta RegEx. Personalmente, soy un gran admirador de RegEx para este tipo de datos. En mi opinión, es mucho más poderoso que simplemente dividir cadenas usando un delimitador, ya que puede elegir ser mucho más inteligente sobre lo que constituye datos “válidos” si lo desea.
Entonces, está incluyendo la totalidad de un comparador de expresiones regulares en su código solo para dividir una cadena. Triste…
– usuario6516765
9 oct 2018 a las 11:28
@Dev No, incluido un comparador de expresiones regulares para ser más inteligente sobre lo que constituye datos válidos, por ejemplo, seleccionar números, y también permitir otros separadores como puntos o guiones
– Ben Cottrell
9 oct 2018 a las 13:53
Esto es malo tanto en términos de tamaño binario como de eficiencia general, pero dado que ambos no son motivo de preocupación en este caso, no continuaré.
– usuario6516765
9 oct 2018 a las 15:23
@Dev Si uno tiene restricciones tan extremas sobre el tamaño binario, entonces debería reconsiderar incluso usar C++, o al menos sus bibliotecas estándar como string/vector/etc porque todas tendrán un efecto similar. En cuanto a la eficiencia, el mejor consejo sería el de Donald Knuth: “La optimización prematura es la raíz de todos los males”; en otras palabras, antes de realizar optimizaciones, la primera tarea es identificar si existe un problema y luego identificar la causa por medios objetivos, como la creación de perfiles, en lugar de perder el tiempo tratando de buscar todas las microoptimizaciones posibles.
– Ben Cottrell
9 oct 2018 a las 16:14
“ambos no son preocupaciones en absoluto en este caso” – yo mismo.
– usuario6516765
9 oct 2018 a las 17:59
Otra posibilidad es imbuir una transmisión con una configuración regional que use un ctype faceta. Una transmisión usa la faceta ctype para determinar qué es un “espacio en blanco”, que trata como separadores. Con una faceta ctype que clasifica su carácter separador como espacio en blanco, la lectura puede ser bastante trivial. Aquí hay una forma de implementar la faceta:
Usamos eso usando imbue para decirle a una transmisión que use una configuración regional que la incluya, luego lea los datos de esa transmisión:
std::istringstream in("07/3/2011");
in.imbue(std::locale(std::locale(), new field_reader);
Con eso en su lugar, la división se vuelve casi trivial: simplemente inicialice un vector usando un par de istream_iterators para leer las piezas de la cadena (que está incrustada en el istringstream):
Obviamente, esto tiende a exagerar si solo lo usa en un lugar. Sin embargo, si lo usa mucho, puede contribuir en gran medida a mantener el resto del código bastante limpio.
Compilador humano
Como nadie ha publicado esto todavía: la solución c ++ 20 es muy simple usando ranges. Puedes usar un std::ranges::views::split para dividir la entrada, y luego transformar la entrada en std::string o std::string_view elementos.
#include <ranges>
...
// The input to transform
const auto str = std::string{"Hello World"};
// Function to transform a range into a std::string
// Replace this with 'std::string_view' to make it a view instead.
auto to_string = [](auto&& r) -> std::string {
const auto data = &*r.begin();
const auto size = static_cast<std::size_t>(std::ranges::distance(r));
return std::string{data, size};
};
const auto range = str |
std::ranges::views::split(' ') |
std::ranges::views::transform(to_string);
for (auto&& token : str | range) {
// each 'token' is the split string
}
Este enfoque puede componer de manera realista casi cualquier cosa, incluso un simple split función que devuelve un std::vector<std::string>:
auto split(const std::string& str, char delimiter) -> std::vector<std::string>
{
const auto range = str |
std::ranges::views::split(delimiter) |
std::ranges::views::transform(to_string);
return {std::ranges::begin(range), std::ranges::end(range)};
}
1. ¿Por qué usas str | range en lugar de range? 2. es transform con to_string ¿necesario? Parece token se puede declarar como string_view así que eso transform es innecesario 3. split_view‘s begin y end las funciones no son constantes, por lo que parece que el programa está mal formado ya que el rango para el ciclo usa un rango constante.
Esto es algo difícil de leer, no está nada claro en comparación con las otras respuestas.
– usuario8143588
19 de diciembre de 2021 a las 4:37
alejandro bertinelli
inherentemente me desagrada stringstream, aunque no estoy seguro de por qué. Hoy, escribí esta función para permitir dividir un std::string por cualquier carácter arbitrario o cadena 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 por la que se dividió 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 == std::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)));
}
}
Para usar, simplemente llame 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 stringstreamsin bibliotecas de terceros, sin volver a C!
1. ¿Por qué usas str | range en lugar de range? 2. es transform con to_string ¿necesario? Parece token se puede declarar como string_view así que eso transform es innecesario 3. split_view‘s begin y end las funciones no son constantes, por lo que parece que el programa está mal formado ya que el rango para el ciclo usa un rango constante.
Esta web utiliza cookies propias y de terceros para su correcto funcionamiento y para fines analíticos y para mostrarte publicidad relacionada con sus preferencias en base a un perfil elaborado a partir de tus hábitos de navegación. Al hacer clic en el botón Aceptar, acepta el uso de estas tecnologías y el procesamiento de tus datos para estos propósitos.
Configurar y más información
posible duplicado de Splitting String C++
– Bo Person
8 de abril de 2012 a las 6:56
¿Responde esto a tu pregunta? ¿Cómo itero sobre las palabras de una cadena?
– xskxzr
28/07/2021 a las 10:00