Je veux convertir un std::string
en minuscule. Je connais la fonction tolower()
, mais dans le passé, j'ai eu des problèmes avec cette fonction et ce n'est pas idéal de toute façon car l'utilisation avec un std::string
nécessiterait une itération sur chaque caractère.
Existe-t-il une alternative qui fonctionne à 100% du temps?
Réponses:
Adapté des questions les moins fréquemment posées :
Vous n'allez vraiment pas vous en sortir sans parcourir chaque personnage. Il n'y a aucun moyen de savoir si le caractère est en minuscule ou en majuscule sinon.
Si vous détestez vraiment
tolower()
, voici une alternative spécialisée uniquement en ASCII que je ne vous recommande pas d'utiliser:Sachez que
tolower()
cela ne peut faire qu'une substitution de caractère par octet unique, ce qui ne convient pas à de nombreux scripts, surtout si vous utilisez un codage multi-octet comme UTF-8.la source
char
à::tolower(int)
.) Vous devez vous assurer de ne pas transmettre une valeur négative.::tolower
pourrait bien planter, c'est UB pour une entrée non-ASCII.Boost fournit un algorithme de chaîne pour cela :
Ou, pour les non-en place :
la source
to_lower_copy
tl; dr
Utilisez la bibliothèque ICU . Si vous ne le faites pas, votre routine de conversion se brisera silencieusement sur des cas dont vous n'êtes probablement pas au courant.
Vous devez d'abord répondre à une question: quel est l' encodage de votre
std::string
? Est-ce ISO-8859-1? Ou peut-être ISO-8859-8? Ou Windows Codepage 1252? Est-ce que tout ce que vous utilisez pour convertir des majuscules en minuscules le sait? (Ou échoue-t-il misérablement pour les personnages0x7f
?)Si vous utilisez UTF-8 (le seul choix sensé parmi les encodages 8 bits) avec
std::string
comme conteneur, vous vous trompez déjà en pensant que vous contrôlez toujours les choses, car vous stockez une séquence de caractères multi-octets dans un conteneur qui n'est pas au courant du concept multi-octets. Même quelque chose d'aussi simple qu'une.substr()
bombe à retardement. (Parce que le fractionnement d'une séquence multi-octets entraînera une (sous-) chaîne invalide.)Et dès que vous essayez quelque chose comme
std::toupper( 'ß' )
, dans n'importe quel encodage, vous êtes en grande difficulté. (Parce qu'il n'est tout simplement pas possible de faire cela "correctement" avec la bibliothèque standard, qui ne peut fournir qu'un seul caractère de résultat, pas le caractère"SS"
nécessaire ici.) [1] Un autre exemple seraitstd::tolower( 'I' )
, qui devrait donner des résultats différents selon les paramètres régionaux . En Allemagne, ce'i'
serait correct; en Turquie,'ı'
(LATINE LETTRE MINUSCULE DOTLESS I) est le résultat attendu (qui, encore une fois, est de plus d'un octet dans le codage UTF-8). Encore un autre exemple est le Sigma grec , en majuscule'∑'
, en minuscule'σ'
... sauf à la fin d'un mot, où il est'ς'
.Ainsi, toute conversion de cas qui fonctionne sur un caractère à la fois, ou pire, un octet à la fois, est cassée par conception.
Ensuite, il y a le point que la bibliothèque standard, pour ce qu'elle est capable de faire, dépend des paramètres régionaux pris en charge sur la machine sur laquelle votre logiciel s'exécute ... et que faites-vous si ce n'est pas le cas?
Donc, ce que vous cherchez vraiment, c'est une classe de chaînes capable de gérer tout cela correctement, et ce n'est pas une des
std::basic_string<>
variantes .(Remarque C ++ 11:
std::u16string
etstd::u32string
sont meilleurs , mais toujours pas parfaits. C ++ 20 apportéstd::u8string
, mais tout cela ne fait que spécifier l'encodage. À bien d'autres égards, ils ignorent encore la mécanique Unicode, comme la normalisation, le classement, .. .)Alors que Boost a l' air sympa, API sage, Boost.Locale est essentiellement un wrapper autour de l' ICU . Si Boost est compilé avec le support ICU ... si ce n'est pas le cas, Boost.Locale est limité au support local compilé pour la bibliothèque standard.
Et croyez-moi, obtenir Boost pour compiler avec ICU peut parfois être une vraie douleur. (Il n'y a pas de binaires précompilés pour Windows, vous devez donc les fournir avec votre application, ce qui ouvre une toute nouvelle boîte de vers ...)
Donc, personnellement, je recommanderais d'obtenir un support Unicode complet directement de la bouche du cheval et d'utiliser directement la bibliothèque ICU :
Compilez (avec G ++ dans cet exemple):
Cela donne:
Notez que la conversion Σ <-> σ au milieu du mot et la conversion Σ <-> ς à la fin du mot. Aucune
<algorithm>
solution basée sur le système ne peut vous apporter cela.[1] En 2017, le Conseil de l'orthographe allemande a jugé que "ẞ" U + 1E9E LETTRE MAJUSCULE LATINE SHARP S pouvait être utilisé officiellement, en option à côté de la conversion traditionnelle "SS" pour éviter toute ambiguïté, par exemple dans les passeports (où les noms sont en majuscules) ). Mon bel exemple, rendu obsolète par décision du comité ...
la source
toupper
ettolower
fonctionnent toujours sur des personnages uniques. La classe de chaîne n'a toujours pas de notion de normalisation (par exemple, si un "ü" est codé comme "u avec tréma" ou "u + combinaison de tréma") ou où une chaîne peut ou non être séparée. La liste continue. u8string est (comme les autres classes de chaînes standard) approprié pour le "passage". Mais si vous voulez traiter Unicode, vous avez besoin d' ICU.En utilisant la boucle basée sur une plage de C ++ 11, un code plus simple serait:
la source
Si la chaîne contient des caractères UTF-8 en dehors de la plage ASCII, alors boost :: algorithm :: to_lower ne les convertira pas. Mieux utiliser boost :: locale :: to_lower lorsque UTF-8 est impliqué. Voir http://www.boost.org/doc/libs/1_51_0/libs/locale/doc/html/conversions.html
la source
Il s'agit d'un suivi de la réponse de Stefan Mai: si vous souhaitez placer le résultat de la conversion dans une autre chaîne, vous devez pré-allouer son espace de stockage avant d'appeler
std::transform
. Étant donné que STL stocke les caractères transformés à l'itérateur de destination (en l'incrémentant à chaque itération de la boucle), la chaîne de destination ne sera pas automatiquement redimensionnée et vous risquez de piétiner la mémoire.la source
Une autre approche utilisant une plage basée sur une boucle avec une variable de référence
la source
Pour autant que je vois, les bibliothèques Boost sont vraiment mauvaises en termes de performances. J'ai testé leur unordered_map en STL et c'était en moyenne 3 fois plus lent (meilleur cas 2, pire 10 fois). Cet algorithme semble également trop faible.
La différence est si grande que je suis sûr que tout ajout que vous devrez faire pour
tolower
le rendre égal à boost "pour vos besoins" sera bien plus rapide que boost.J'ai fait ces tests sur un Amazon EC2, donc les performances ont varié pendant le test mais vous avez toujours l'idée.
-O2
fait comme ça:La source:
Je suppose que je devrais faire les tests sur une machine dédiée mais j'utiliserai cet EC2 donc je n'ai pas vraiment besoin de le tester sur ma machine.
la source
Le moyen le plus simple de convertir une chaîne en minuscules sans se soucier de l'espace de noms std est le suivant
1: chaîne avec / sans espaces
2: chaîne sans espaces
la source
std::ctype::tolower()
à partir de la bibliothèque de localisation C ++ standard le fera correctement pour vous. Voici un exemple extrait de la page de référence tolowerla source
const
? Cela semble le rendre un peu plus compliqué (par exemple, il ne semble pas que vous puissiez l'utiliserf.tolower()
), car vous devez mettre les caractères dans une nouvelle chaîne. Souhaitez-vous utilisertransform()
et quelque chose commestd::bind1st( std::mem_fun() )
pour l'opérateur?tolower
aveclocale
paramètre, l'appel implicite àuse_facet
semble être un goulot d'étranglement des performances. Un de mes collègues a obtenu une augmentation de vitesse de plusieurs 100% en remplaçantboost::iequals
(ce qui pose ce problème) par une version quiuse_facet
n'est appelée qu'une seule fois en dehors de la boucle.Une alternative à Boost est POCO (pocoproject.org).
POCO propose deux variantes:
Les versions "sur place" ont toujours "InPlace" dans le nom.
Les deux versions sont illustrées ci-dessous:
la source
Il existe un moyen de convertir les majuscules en minuscules SANS faire de tests if , et c'est assez simple. L'utilisation de clocale.h par la fonction / macro isupper () devrait prendre en charge les problèmes liés à votre emplacement, mais sinon, vous pouvez toujours ajuster l'UtoL [] au contenu de votre cœur.
Étant donné que les caractères de C ne sont en réalité que des entiers 8 bits (en ignorant les jeux de caractères larges pour le moment), vous pouvez créer un tableau de 256 octets contenant un autre jeu de caractères et dans la fonction de conversion, utilisez les caractères de votre chaîne comme indices dans le tableau de conversion.
Cependant, au lieu d'un mappage 1 pour 1, donnez aux membres du tableau en majuscules les valeurs BYTE int pour les caractères en minuscule. Vous pouvez trouver islower () et isupper () utiles ici.
Le code ressemble à ceci ...
Cette approche vous permettra en même temps de remapper tout autre personnage que vous souhaitez modifier.
Cette approche a un énorme avantage lors de l'exécution sur des processeurs modernes, il n'est pas nécessaire de faire de prédiction de branche car il n'y a pas de tests si comprenant une ramification. Cela permet d'économiser la logique de prédiction de branche du processeur pour les autres boucles et a tendance à éviter les blocages de pipeline.
Certains ici peuvent reconnaître cette approche comme la même que celle utilisée pour convertir EBCDIC en ASCII.
la source
Puisqu'aucune des réponses n'a mentionné la prochaine bibliothèque Ranges, qui est disponible dans la bibliothèque standard depuis C ++ 20, et actuellement disponible séparément sur GitHub en tant que
range-v3
, je voudrais ajouter un moyen d'effectuer cette conversion en l'utilisant.Pour modifier la chaîne sur place:
Pour générer une nouvelle chaîne:
(N'oubliez pas
#include <cctype>
et les en-têtes de gammes requis.)Remarque: l'utilisation de
unsigned char
comme argument pour le lambda est inspirée de cppreference , qui stipule:la source
Mes propres fonctions de modèle qui exécutent les majuscules / minuscules.
la source
towlower
pour les caractères larges qui prend en charge l'UTF-16.Voici une macro technique si vous voulez quelque chose de simple:
Cependant, notez que le commentaire de @ AndreasSpindler sur cette réponse est toujours une considération importante, cependant, si vous travaillez sur quelque chose qui n'est pas seulement des caractères ASCII.
la source
void strtoupper(std::string& x) { std::transform (x.begin(), x.end(), x.begin(), ::toupper); }
x
pourrait être une expression valide, qui arrive à se compiler correctement mais donnera des résultats complètement faux à cause des macros.Pour plus d'informations: http://www.cplusplus.com/reference/locale/tolower/
la source
Non
Il y a plusieurs questions que vous devez vous poser avant de choisir une méthode de minuscule.
Une fois que vous avez répondu à ces questions, vous pouvez commencer à chercher une solution qui correspond à vos besoins. Il n'y a pas de taille unique qui fonctionne pour tout le monde partout!
la source
Essayez cette fonction :)
la source
Sur les plates-formes Microsoft, vous pouvez utiliser la
strlwr
famille de fonctions: http://msdn.microsoft.com/en-us/library/hkxwh33z.aspxla source
Extrait de code
la source
Utilisez fplus :: to_lower_case ().
(fplus: https://github.com/Dobiasd/FunctionalPlus .
Recherchez 'to_lower_case' sur http://www.editgym.com/fplus-api-search/ )
la source
Copiez car il n'a pas été autorisé d'améliorer la réponse. Merci beaucoup
Explication:
for(auto& c : test)
est une boucle for basée sur une plage du type :for (
range_declaration
:
range_expression
)
loop_statement
range_declaration
:auto& c
Ici, le spécificateur automatique est utilisé pour la déduction automatique de type. Le type est donc déduit de l'initialiseur de variables.
range_expression
:test
La plage dans ce cas sont les caractères de la chaîne
test
.Les caractères de la chaîne
test
sont disponibles comme référence dans l'identificateur de boucle forc
.la source
C ++ n'a pas de méthodes tolower ou toupper implémentées pour la chaîne, mais il est disponible pour char. On peut facilement lire chaque caractère de chaîne, le convertir en casse requis et le remettre en chaîne. Un exemple de code sans utiliser de bibliothèque tierce:
Pour une opération basée sur des caractères sur une chaîne: pour chaque caractère de la chaîne
la source
Cela pourrait être une autre version simple pour convertir les majuscules en minuscules et vice versa. J'ai utilisé la version communautaire VS2017 pour compiler ce code source.
Remarque: s'il y a des caractères spéciaux, vous devez les traiter à l'aide de la vérification des conditions.
la source
J'ai essayé std :: transform, tout ce que j'obtiens est une erreur de compilation criptic abominable que seuls les druides d'il y a 200 ans peuvent comprendre (ne peut pas convertir de en flibidi flabidi flu)
cela fonctionne bien et peut être facilement modifié
la source