Quiero convertir un std::string
a minúscula. Soy consciente de la función tolower()
, sin embargo, en el pasado he tenido problemas con esta función y de todos modos no es lo ideal, ya que usarla con std::string
requeriría iterar sobre cada carácter.
¿Existe alguna alternativa que funcione el 100% del tiempo?
c++
string
c++-standard-library
tolower
Konrad
fuente
fuente
Respuestas:
Adaptado de preguntas no tan frecuentes :
Realmente no vas a escapar sin recorrer cada personaje. No hay forma de saber si el carácter es minúscula o mayúscula.
Si realmente odias
tolower()
, aquí hay una alternativa ASCII especializada que no recomiendo que uses:Tenga en cuenta que
tolower()
solo puede realizar una sustitución por carácter de un solo byte, lo que no es adecuado para muchos scripts, especialmente si se utiliza una codificación de varios bytes como UTF-8.fuente
char
a::tolower(int)
). Debe asegurarse de no pasar un valor negativo.::tolower
bien puede bloquearse, es UB para entrada no ASCII.Boost proporciona un algoritmo de cadena para esto :
O, para no estar en el lugar :
fuente
to_lower_copy
tl; dr
Use la biblioteca de la UCI . Si no lo hace, su rutina de conversión se interrumpirá silenciosamente en casos de los que probablemente ni siquiera esté al tanto.
Primero tienes que responder una pregunta: ¿Cuál es la codificación de tu
std::string
? ¿Es ISO-8859-1? O tal vez ISO-8859-8? ¿O la página de códigos de Windows 1252? ¿Lo que sea que esté usando para convertir mayúsculas a minúsculas lo sabe? (¿O falla miserablemente para los personajes0x7f
?)Si está utilizando UTF-8 (la única opción sensata entre las codificaciones de 8 bits)
std::string
como contenedor, ya se está engañando a sí mismo al creer que todavía tiene el control de las cosas, porque está almacenando una secuencia de caracteres multibyte en un contenedor que no conoce el concepto multibyte. Incluso algo tan simple como.substr()
una bomba de relojería. (Debido a que dividir una secuencia multibyte dará como resultado una (sub) cadena no válida).Y tan pronto como intente algo como
std::toupper( 'ß' )
, en cualquier codificación, estará en serios problemas. (Debido a que simplemente no es posible hacer esto "correctamente" con la biblioteca estándar, que solo puede entregar un carácter de resultado, no el"SS"
necesario aquí.) [1] Otro ejemplo seríastd::tolower( 'I' )
, que debería producir resultados diferentes dependiendo de la configuración regional . En Alemania,'i'
sería correcto; en Turquía,'ı'
(LETRA PEQUEÑA LATINA DOTLESS I) es el resultado esperado (que, nuevamente, es más de un byte en la codificación UTF-8). Otro ejemplo es el Sigma griego , mayúsculas'∑'
, minúsculas'σ'
... excepto al final de una palabra, donde está'ς'
.Entonces, cualquier conversión de caso que funcione en un personaje a la vez, o peor, un byte a la vez, se rompe por diseño.
Luego está el punto de que la biblioteca estándar, por lo que es capaz de hacer, depende de qué configuraciones regionales sean compatibles con la máquina en la que se está ejecutando su software ... ¿y qué hace si no lo es?
Entonces, lo que realmente está buscando es una clase de cadena que sea capaz de lidiar con todo esto correctamente, y esa no es ninguna de las
std::basic_string<>
variantes .(Nota de C ++ 11:
std::u16string
ystd::u32string
son mejores , pero aún no son perfectos. C ++ 20 traídostd::u8string
, pero todo lo que hacen es especificar la codificación. En muchos otros aspectos, siguen ignorando la mecánica Unicode, como la normalización, la clasificación, ... .)Mientras que Boost se ve bien, API sabia, Boost.Locale es básicamente un contenedor alrededor de la UCI . Si Boost se compila con soporte de ICU ... si no es así, Boost.Locale se limita al soporte de entorno local compilado para la biblioteca estándar.
Y créanme, hacer que Boost compile con UCI puede ser un verdadero dolor a veces. (No hay binarios precompilados para Windows, por lo que tendría que proporcionarlos junto con su aplicación, y eso abre una nueva lata de gusanos ...)
Entonces, personalmente, recomendaría obtener soporte completo de Unicode directamente de la boca del caballo y usar la biblioteca de la UCI directamente:
Compilar (con G ++ en este ejemplo):
Esto da:
Tenga en cuenta que la conversión Σ <-> σ en el medio de la palabra, y la conversión Σ <-> ς al final de la palabra. Ninguna
<algorithm>
solución basada en eso puede darte eso.[1] En 2017, el Consejo de Ortografía Alemana dictaminó que "ẞ" U + 1E9E LATIN CAPITAL LETTER SHARP S podría usarse oficialmente, como una opción junto a la conversión tradicional "SS" para evitar la ambigüedad, por ejemplo, en pasaportes (donde los nombres están en mayúscula) ) Mi hermoso ejemplo, que quedó obsoleto por decisión del comité ...
fuente
toupper
ytolower
aún funcionan en caracteres individuales. La clase de cadena aún no tiene una noción de normalización (por ejemplo, si una "ü" está codificada como "u con diaeresis" o "u + diaeresis combinada") o donde una cadena puede o no estar separada. La lista continua. u8string es (como las otras clases de cadena estándar) apropiado para "pasar a través". Pero si desea procesar Unicode, necesita una UCI.Usando el rango para el ciclo de C ++ 11, un código más simple sería:
fuente
Si la cadena contiene caracteres UTF-8 fuera del rango ASCII, entonces boost :: algoritm :: to_lower no los convertirá. Mejor use boost :: locale :: to_lower cuando UTF-8 esté involucrado. Ver http://www.boost.org/doc/libs/1_51_0/libs/locale/doc/html/conversions.html
fuente
Este es un seguimiento de la respuesta de Stefan Mai: si desea colocar el resultado de la conversión en otra cadena, debe preasignar su espacio de almacenamiento antes de llamar
std::transform
. Dado que STL almacena los caracteres transformados en el iterador de destino (incrementándolo en cada iteración del bucle), la cadena de destino no se redimensionará automáticamente y corre el riesgo de pisotear la memoria.fuente
Otro enfoque que utiliza el rango basado para el bucle con la variable de referencia
fuente
Por lo que veo, las bibliotecas de Boost son realmente malas en cuanto al rendimiento. He probado su unordered_map a STL y fue un promedio 3 veces más lento (el mejor caso 2, el peor fue 10 veces). Además, este algoritmo parece demasiado bajo.
La diferencia es tan grande que estoy seguro de que cualquier adición que necesite hacer para
tolower
igualar el impulso "para sus necesidades" será mucho más rápido que el impulso.He realizado estas pruebas en un Amazon EC2, por lo tanto, el rendimiento varió durante la prueba, pero aún se entiende.
-O2
lo hizo así:Fuente:
Supongo que debería hacer las pruebas en una máquina dedicada, pero usaré este EC2, así que realmente no necesito probarlo en mi máquina.
fuente
La manera más simple de convertir cadenas en minúsculas sin preocuparse por el espacio de nombres estándar es la siguiente
1: cadena con / sin espacios
2: cadena sin espacios
fuente
std::ctype::tolower()
de la biblioteca estándar de localización de C ++ lo hará correctamente por usted. Aquí hay un ejemplo extraído de la página de referencia de tolowerfuente
const
? Eso parece hacerlo un poco más desordenado (por ejemplo, no parece que pueda usarf.tolower()
), ya que necesita colocar los caracteres en una nueva cadena. ¿Usaríastransform()
algo así comostd::bind1st( std::mem_fun() )
para el operador?tolower
conlocale
parámetro, la llamada implícita ause_facet
parece ser un cuello de botella de rendimiento. Uno de mis compañeros de trabajo ha logrado un aumento de velocidad del 100% al reemplazarboost::iequals
(que tiene este problema) con una versión dondeuse_facet
solo se llama una vez fuera del bucle.Una alternativa a Boost es POCO (pocoproject.org).
POCO ofrece dos variantes:
Las versiones "In Place" siempre tienen "InPlace" en el nombre.
Ambas versiones se muestran a continuación:
fuente
Hay una manera de convertir mayúsculas a minúsculas SIN hacer pruebas if , y es bastante sencillo. El uso de la función isupper () / macro de clocale.h debería solucionar los problemas relacionados con su ubicación, pero si no, siempre puede modificar la UtoL [] al contenido de su corazón.
Dado que los caracteres de C son solo entradas de 8 bits (ignorando los juegos de caracteres anchos por el momento), puede crear una matriz de 256 bytes que contenga un conjunto alternativo de caracteres, y en la función de conversión use los caracteres en su cadena como subíndices en el matriz de conversión
Sin embargo, en lugar de una asignación 1 por 1, proporcione a los miembros de la matriz en mayúsculas los valores BYTE int para los caracteres en minúsculas. Puede encontrar islower () e isupper () útiles aquí.
El código se ve así ...
Este enfoque, al mismo tiempo, le permitirá reasignar cualquier otro personaje que desee cambiar.
Este enfoque tiene una gran ventaja cuando se ejecuta en procesadores modernos, no hay necesidad de hacer predicciones de ramificación ya que no hay pruebas de ramificación. Esto guarda la lógica de predicción de bifurcación de la CPU para otros bucles y tiende a evitar paradas de canalización.
Algunos aquí pueden reconocer este enfoque como el mismo utilizado para convertir EBCDIC a ASCII.
fuente
Como ninguna de las respuestas mencionó la próxima biblioteca de Rangos, que está disponible en la biblioteca estándar desde C ++ 20, y actualmente está disponible por separado en GitHub como
range-v3
, me gustaría agregar una forma de realizar esta conversión usándola.Para modificar la cadena en el lugar:
Para generar una nueva cadena:
(No olvide
#include <cctype>
y los encabezados de Rangos requeridos).Nota: el uso de
unsigned char
como argumento para la lambda está inspirado en cppreference , que establece:fuente
Mis propias funciones de plantilla que realizan mayúsculas / minúsculas.
fuente
towlower
para caracteres anchos que admite el UTF-16.Aquí hay una técnica de macro si quieres algo simple:
Sin embargo, tenga en cuenta que el comentario de @ AndreasSpindler sobre esta respuesta sigue siendo una consideración importante, sin embargo, si está trabajando en algo que no son solo caracteres ASCII.
fuente
void strtoupper(std::string& x) { std::transform (x.begin(), x.end(), x.begin(), ::toupper); }
x
podría ser una expresión válida, que solo se compila correctamente pero dará resultados completamente falsos debido a las macros.Para más información: http://www.cplusplus.com/reference/locale/tolower/
fuente
No
Hay varias preguntas que debe hacerse antes de elegir un método en minúsculas.
Una vez que tenga respuestas a esas preguntas, puede comenzar a buscar una solución que se adapte a sus necesidades. ¡No hay una talla única que funcione para todos en todas partes!
fuente
Prueba esta función :)
fuente
En las plataformas de Microsoft puede usar la
strlwr
familia de funciones: http://msdn.microsoft.com/en-us/library/hkxwh33z.aspxfuente
Fragmento de código
fuente
Use fplus :: to_lower_case ().
(fplus: https://github.com/Dobiasd/FunctionalPlus .
Busque 'to_lower_case' en http://www.editgym.com/fplus-api-search/ )
fuente
Copie porque no se permitió mejorar la respuesta. Gracias
Explicación:
for(auto& c : test)
es un bucle basado en rango del tipo :for (
range_declaration
:
range_expression
)
loop_statement
range_declaration
:auto& c
Aquí se utiliza el especificador automático para la deducción automática de tipo. Entonces el tipo se deduce del inicializador de variables.
range_expression
:test
El rango en este caso son los caracteres de la cadena
test
.Los caracteres de la cadena
test
están disponibles como referencia dentro del identificador de bucle forc
.fuente
C ++ no tiene implementados los métodos tolower o toupper para string, pero está disponible para char. Uno puede leer fácilmente cada carácter de cadena, convertirlo en el caso requerido y volver a colocarlo en cadena. Un código de muestra sin usar ninguna biblioteca de terceros:
Para operación basada en caracteres en cadena: para cada carácter en cadena
fuente
Esta podría ser otra versión simple para convertir mayúsculas a minúsculas y viceversa. Usé la versión de comunidad VS2017 para compilar este código fuente.
Nota: si hay caracteres especiales, entonces deben manejarse utilizando la verificación de condición.
fuente
Intenté std :: transform, todo lo que obtengo es un error abominable de compilación stl criptic que solo los druidas de hace 200 años pueden entender (no se puede convertir en gripe flibidi flabidi)
esto funciona bien y se puede ajustar fácilmente
fuente