Estoy tratando de iterar sobre las palabras de una cadena.
Se puede suponer que la cadena está compuesta de palabras separadas por espacios en blanco.
Tenga en cuenta que no estoy interesado en las funciones de cadena C o ese tipo de manipulación / acceso de caracteres. Además, dé prioridad a la elegancia sobre la eficiencia en su respuesta.
La mejor solución que tengo en este momento es:
#include <iostream>
#include <sstream>
#include <string>
using namespace std;
int main()
{
string s = "Somewhere down the road";
istringstream iss(s);
do
{
string subs;
iss >> subs;
cout << "Substring: " << subs << endl;
} while (iss);
}
¿Hay alguna forma más elegante de hacer esto?
while (iss) { string subs; iss >> subs; cout << "Substring: " << sub << endl; }
string sub; while (iss >> sub) cout << "Substring: " << sub << '\n';
Respuestas:
Para lo que vale, aquí hay otra forma de extraer tokens de una cadena de entrada, confiando solo en las instalaciones estándar de la biblioteca. Es un ejemplo del poder y la elegancia detrás del diseño del STL.
En lugar de copiar los tokens extraídos en una secuencia de salida, uno podría insertarlos en un contenedor, utilizando el mismo genérico
copy
algoritmo .... o crea el
vector
directamente:fuente
Lo uso para dividir la cadena por un delimitador. El primero pone los resultados en un vector preconstruido, el segundo devuelve un nuevo vector.
Tenga en cuenta que esta solución no omite los tokens vacíos, por lo que a continuación encontrará 4 elementos, uno de los cuales está vacío:
fuente
empty()
verifique:if (!item.empty()) elems.push_back(item)
->
?f(split(s, d, v))
sin dejar de tener el beneficio de una preasignaciónvector
si lo desea.Una posible solución con Boost podría ser:
Este enfoque podría ser incluso más rápido que el
stringstream
enfoque. Y dado que esta es una función de plantilla genérica, puede usarse para dividir otros tipos de cadenas (wchar, etc. o UTF-8) usando todo tipo de delimitadores.Consulte la documentación para más detalles.
fuente
fuente
getline
en lawhile
condición, por ejemplo, para dividir por comas, usewhile(getline(ss, buff, ','))
.Para aquellos con quienes no se siente bien sacrificar toda la eficiencia por el tamaño del código y ver lo "eficiente" como un tipo de elegancia, lo siguiente debería llegar a un punto óptimo (y creo que la clase de contenedor de plantillas es una adición increíblemente elegante):
Por lo general, elijo usar
std::vector<std::string>
tipos como mi segundo parámetro (ContainerT
) ... perolist<>
es mucho más rápido quevector<>
cuando no se necesita acceso directo, e incluso puede crear su propia clase de cadena y usar algo comostd::list<subString>
dondesubString
no hace ninguna copia a una velocidad increíble aumentaEs más del doble de rápido que el tokenize más rápido en esta página y casi 5 veces más rápido que otros. Además, con los tipos de parámetros perfectos, puede eliminar todas las cadenas y copias de listas para aumentar la velocidad adicional.
Además, no hace el retorno (extremadamente ineficiente) del resultado, sino que pasa los tokens como referencia, lo que también le permite construir tokens utilizando múltiples llamadas si así lo desea.
Por último, le permite especificar si se recortarán tokens vacíos de los resultados a través de un último parámetro opcional.
Todo lo que necesita es
std::string
... el resto son opcionales. No utiliza secuencias ni la biblioteca de impulso, pero es lo suficientemente flexible como para poder aceptar algunos de estos tipos foráneos de forma natural.fuente
typedef ContainerT Base; typedef typename Base::value_type ValueType; typedef typename ValueType::size_type SizeType;
Luego, sustituya value_type y size_types en consecuencia.trimEmpty = true
. Tenga en cuenta que"abo"
no es un delimitador en esta respuesta, sino la lista de caracteres delimitadores. Sería simple modificarlo para tomar una sola cadena de caracteres delimitadores (creo questr.find_first_of
debería cambiar astr.find_first
, pero podría estar equivocado ... no puedo probar)Aquí hay otra solución. Es compacto y razonablemente eficiente:
Se puede templar fácilmente para manejar separadores de cuerdas, cadenas anchas, etc.
Tenga en cuenta que la división
""
da como resultado una sola cadena vacía y la división","
(es decir, sep) da como resultado dos cadenas vacías.También se puede expandir fácilmente para omitir tokens vacíos:
Si se desea dividir una cadena en varios delimitadores mientras se omiten los tokens vacíos, se puede usar esta versión:
fuente
Esta es mi forma favorita de iterar a través de una cadena. Puedes hacer lo que quieras por palabra.
fuente
word
como achar
?stringstream ss("Hello World, this is*@#&$(@ a string"); char c; while(ss >> c) cout << c;
Esto es similar a la pregunta de desbordamiento de pila ¿ Cómo tokenizo una cadena en C ++? .
fuente
Me gusta lo siguiente porque pone los resultados en un vector, admite una cadena como delimitación y da control sobre el mantenimiento de valores vacíos. Pero, no se ve tan bien entonces.
Por supuesto, Boost tiene una
split()
que funciona parcialmente de esa manera. Y, si por 'espacio en blanco', realmente se refiere a cualquier tipo de espacio en blanco, usar la división de Boost conis_any_of()
funciona muy bien.fuente
El STL no tiene dicho método disponible ya.
Sin embargo, puede usar la
strtok()
función de C utilizando elstd::string::c_str()
miembro, o puede escribir la suya propia. Aquí hay una muestra de código que encontré después de una búsqueda rápida en Google ( "división de cadena STL" ):Tomado de: http://oopweb.com/CPP/Documents/CPPHOWTO/Volume/C++Programming-HOWTO-7.html
Si tiene preguntas sobre el ejemplo de código, deje un comentario y se lo explicaré.
Y solo porque no implemente un
typedef
iterador llamado o sobrecarga el<<
operador no significa que sea un código incorrecto. Yo uso las funciones de C con bastante frecuencia. Por ejemplo,printf
yscanf
ambos son más rápidos que (std::cin
ystd::cout
significativamente), lafopen
sintaxis es mucho más amigable para los tipos binarios, y también tienden a producir EXE más pequeños.No se deje vender en este acuerdo de "Elegancia sobre rendimiento" .
fuente
Aquí hay una función dividida que:
ignora los tokens vacíos (se pueden cambiar fácilmente)
Ejemplo de uso:
fuente
Tengo una solución de 2 líneas para este problema:
Luego, en lugar de imprimir, puede ponerlo en un vector.
fuente
Otra forma flexible y rápida.
Para usarlo con un vector de cadenas (Editar: ya que alguien señaló que no heredaría las clases STL ... hrmf;)):
¡Eso es! Y esa es solo una forma de usar el tokenizador, como cómo contar palabras:
Limitado por la imaginación;)
fuente
Appender
nota "¿Por qué no deberíamos heredar una clase de las clases STL?"Aquí hay una solución simple que usa solo la biblioteca de expresiones regulares estándar
El argumento regex permite verificar múltiples argumentos (espacios, comas, etc.)
Por lo general, solo verifico la división en espacios y comas, por lo que también tengo esta función predeterminada:
Las
"[\\s,]+"
comprobaciones de espacios (\\s
) y comas (,
).Tenga en cuenta que si desea dividir en
wstring
lugar destring
,std::regex
astd::wregex
sregex_token_iterator
awsregex_token_iterator
Tenga en cuenta que también puede tomar el argumento de cadena por referencia, dependiendo de su compilador.
fuente
R"([\s,]+)"
.Usarlo
std::stringstream
como lo hace funciona perfectamente bien y hacer exactamente lo que quería. Sin embargo, si solo está buscando una forma diferente de hacer las cosas, puede usarstd::find()
/std::find_first_of()
ystd::string::substr()
.Aquí hay un ejemplo:
fuente
prev_pos = pos += delimiter.length();
Si desea usar boost, pero desea usar una cadena completa como delimitador (en lugar de caracteres individuales como en la mayoría de las soluciones propuestas anteriormente), puede usar el
boost_split_iterator
.Código de ejemplo que incluye una plantilla conveniente:
fuente
Aquí hay una solución de expresiones regulares que solo utiliza la biblioteca de expresiones regulares estándar. (Estoy un poco oxidado, por lo que puede haber algunos errores de sintaxis, pero esta es al menos la idea general)
fuente
Hay una función llamada
strtok
.fuente
strtok
es de la biblioteca estándar de C, no de C ++. No es seguro usarlo en programas multiproceso. Modifica la cadena de entrada.strtok
cuando otro subproceso aún se está procesando, este puntero de caracteres se sobrescribirá y ambos subprocesos tendrán resultados incorrectos. mkssoftware.com/docs/man3/strtok.3.aspEl flujo de cadena puede ser conveniente si necesita analizar la cadena por símbolos que no sean espacios:
fuente
Hasta ahora utilicé el de Boost , pero necesitaba algo que no dependiera de él, así que llegué a esto:
Un buen punto es que
separators
puedes pasar más de un personaje.fuente
He rodado el mío usando strtok y he usado boost para dividir una cadena. El mejor método que he encontrado es la Biblioteca de C ++ String Toolkit . Es increíblemente flexible y rápido.
El kit de herramientas tiene mucha más flexibilidad de lo que muestra este simple ejemplo, pero su utilidad para analizar una cadena en elementos útiles es increíble.
fuente
Corto y elegante
puede usar cualquier cadena como delimitador, también se puede usar con datos binarios (std :: string admite datos binarios, incluidos los nulos)
utilizando:
salida:
fuente
Hice esto porque necesitaba una manera fácil de dividir cadenas y cadenas basadas en c ... Espero que alguien más pueda encontrarlo útil también. Además, no se basa en tokens y puede usar campos como delimitadores, que es otra clave que necesitaba.
Estoy seguro de que se pueden hacer mejoras para mejorar aún más su elegancia y, por favor, háganlo por todos los medios.
StringSplitter.hpp:
StringSplitter.cpp:
Ejemplos:
Saldrá:
Este
es
un
ejemplo de
cstring
Para mantener entradas vacías (por defecto, los vacíos serán excluidos):
El objetivo era hacerlo similar al método Split () de C # donde dividir una cadena es tan fácil como:
Espero que alguien más pueda encontrar esto tan útil como yo.
fuente
¿Qué hay de esto?
fuente
Esta respuesta toma la cadena y la coloca en un vector de cadenas. Utiliza la biblioteca de impulso.
fuente
Aquí hay otra forma de hacerlo ...
fuente
Me gusta utilizar los métodos boost / regex para esta tarea, ya que proporcionan la máxima flexibilidad para especificar los criterios de división.
fuente
Recientemente tuve que dividir una palabra en camello en subpalabras. No hay delimitadores, solo caracteres superiores.
Por ejemplo, esto divide "AQueryTrades" en "A", "Query" y "Trades". La función funciona con cadenas estrechas y anchas. Debido a que respeta la ubicación actual, divide "RaumfahrtÜberwachungsVerordnung" en "Raumfahrt", "Überwachungs" y "Verordnung".
La nota
std::upper
realmente se debe pasar como argumento de plantilla de función. Entonces, el más generalizado de esta función puede dividirse en delimitadores como","
,";"
o" "
también.fuente
std::isupper
podría pasarse como argumento, nostd::upper
. Segundo puesto untypename
antes delString::const_iterator
.fuente
Usando
std::string_view
y Eric Niebler'srange-v3
biblioteca de :https://wandbox.org/permlink/kW5lwRCL1pxjp2pW
Al usar un
for
bucle de rango en lugar de unranges::for_each
algoritmo:fuente