983 votes

Comment convertir une instance de std::string en minuscules ?

Je veux convertir un std::string en minuscules. Je suis conscient de la fonction tolower() . Cependant, dans le passé, j'ai eu des problèmes avec cette fonction et elle n'est de toute façon pas idéale car l'utiliser avec une std::string nécessiterait d'itérer sur chaque caractère.

Existe-t-il une alternative qui fonctionne 100% du temps ?

1102voto

Stefan Mai Points 9477

Adapté de Questions pas si fréquentes :

#include <algorithm>
#include <cctype>
#include <string>

std::string data = "Abc";
std::transform(data.begin(), data.end(), data.begin(),
    [](unsigned char c){ return std::tolower(c); });

Vous ne pourrez vraiment pas vous en sortir sans itérer avec chaque personnage. Il n'y a aucun moyen de savoir si le caractère est minuscule ou majuscule autrement.

Si vous détestez vraiment tolower() voici une alternative spécialisée uniquement en ASCII que je ne vous recommande pas d'utiliser :

char asciitolower(char in) {
    if (in <= 'Z' && in >= 'A')
        return in - ('Z' - 'z');
    return in;
}

std::transform(data.begin(), data.end(), data.begin(), asciitolower);

Sachez que tolower() ne peut faire qu'une substitution par caractère d'un seul octet, ce qui est mal adapté à de nombreux scripts, surtout si l'on utilise un encodage multi-octet comme UTF-8.

372voto

Rob Points 22239

Boost fournit un algorithme de chaîne pour cela :

#include <boost/algorithm/string.hpp>

std::string str = "HELLO, WORLD!";
boost::algorithm::to_lower(str); // modifies str

Ou, pour le non-encastrement :

#include <boost/algorithm/string.hpp>

const std::string str = "HELLO, WORLD!";
const std::string lower_str = boost::algorithm::to_lower_copy(str);

339voto

DevSolar Points 18897

en résumé

Utilisez le Bibliothèque de l'USI . Si vous ne le faites pas, votre routine de conversion se brisera silencieusement sur des cas dont vous n'avez probablement même pas conscience de l'existence.


Vous devez d'abord répondre à une question : Quel est le codage de votre std::string ? Est-ce que c'est ISO-8859-1 ? Ou peut-être ISO-8859-8 ? Ou encore Windows Codepage 1252 ? Est-ce que ce que vous utilisez pour convertir les majuscules en minuscules le sait ? (Ou est-ce qu'il échoue lamentablement pour les personnages sur 0x7f ?)

Si vous utilisez UTF-8 ( le seul choix sensé parmi les encodages 8 bits ) avec std::string en tant que conteneur, vous vous trompez déjà si vous croyez que vous contrôlez encore les choses. Vous stockez une séquence de caractères multi-octets dans un conteneur qui ne connaît pas le concept de multi-octets, pas plus que la plupart des opérations que vous pouvez effectuer sur lui ! Même quelque chose d'aussi simple que .substr() peut donner lieu à des (sous-)chaînes de caractères non valides parce que vous avez coupé au milieu d'une séquence de plusieurs octets.

Dès que vous essayez quelque chose comme std::toupper( 'ß' ) ou std::tolower( 'Σ' ) en tout l'encodage, vous avez des problèmes. Parce que 1), la norme n'opère jamais que sur un seul caractère à la fois, donc elle ne peut simplement pas tourner ß en SS ce qui serait correct. Et 2), la norme n'opère jamais que sur un seul caractère à la fois, elle ne peut donc pas décider si Σ est au milieu d'un mot (où σ serait correct), ou à la fin ( ς ). Un autre exemple serait std::tolower( 'I' ) ce qui devrait donner des résultats différents en fonction de la langue -- pratiquement partout où l'on peut s'attendre i mais en Turquie ı (LATIN SMALL LETTER DOTLESS I) est la bonne réponse (qui, encore une fois, représente plus d'un octet dans l'encodage UTF-8).

Donc, tout conversion de cas qui fonctionne sur un caractère à la fois, ou pire, une octet à la fois, est cassé par conception. Cela comprend tous les std:: variantes existant à l'heure actuelle.

Ensuite, il y a le point que la bibliothèque standard, pour ce qu'elle est es de faire, c'est de dépendre de quelles locales sont supporté par sur la machine sur laquelle votre logiciel est exécuté... et que faites-vous si votre locale cible fait partie de celles qui ne sont pas prises en charge par la machine de votre client ?

Donc ce que vous êtes vraiment est une classe de chaînes de caractères capable de gérer tout cela correctement, et c'est no l'un des std::basic_string<> variantes .

(Note C++11 : std::u16string y std::u32string sont meilleur mais ce n'est pas encore parfait. C++20 a apporté std::u8string mais tout ce qu'ils font, c'est de spécifier le codage . À bien d'autres égards, ils ignorent encore les mécanismes d'Unicode, comme la normalisation, la collation, ...).

Alors que Boost regarde Bien, au niveau de l'API, Boost.Locale est essentiellement une enveloppe autour de UNITÉ DE SOINS INTENSIFS . 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 être une vraie douleur parfois. (Il n'y a pas de binaires pré-compilés pour Windows qui incluent ICU, vous devez donc les fournir avec votre application, et que ouvre une toute nouvelle boîte de vers...)

Personnellement, je recommanderais d'obtenir la prise en charge complète d'Unicode directement de la bouche du cheval et d'utiliser la fonction UNITÉ DE SOINS INTENSIFS directement à la bibliothèque :

#include <unicode/unistr.h>
#include <unicode/ustream.h>
#include <unicode/locid.h>

#include <iostream>

int main()
{
    /*                          "Odysseus" */
    char const * someString = u8"ΟΔΥΣΣΕΥΣ";
    icu::UnicodeString someUString( someString, "UTF-8" );
    // Setting the locale explicitly here for completeness.
    // Usually you would use the user-specified system locale,
    // which *does* make a difference (see ı vs. i above).
    std::cout << someUString.toLower( "el_GR" ) << "\n";
    std::cout << someUString.toUpper( "el_GR" ) << "\n";
    return 0;
}

Compilez (avec G++ dans cet exemple) :

g++ -Wall example.cpp -licuuc -licuio

Cela donne :

ὀδυσσεύς

Notez que la conversion Σ<->σ au milieu du mot, et la conversion Σ<->ς à la fin du mot. Non <algorithm> -Une solution basée sur les technologies de l'information et de la communication peut vous offrir cela.

55voto

Vous pouvez également utiliser la prise en charge de la locale C++ pour cela. Vous gagnez en généricité.

#include <functional>
#include <locale>

std::string a = "ABC";

std::transform(a.begin(), a.end(), a.begin(), 
    std::bind2nd(std::ptr_fun(&std::tolower<char>), std::locale("")));

Si vous avez une gamme de caractères inscriptibles, vous pouvez également faire

std::use_facet< std::ctype<char> >(std::locale("")).tolower(&a[0], &a[0] + a.size());

locale("") construira un objet locale représentant votre locale préférée.

Je recommande la lecture de l'ouvrage de Stroustrup Annexe D : Locales qui est disponible gratuitement et qui donne un bon aperçu des locales en C++.

29voto

Constantin Points 12185

Nota: tolower() ne fonctionne pas 100% du temps.

Les opérations minuscules/majuscules ne s'appliquent qu'aux caractères, et std::string est essentiellement un tableau d'octets, pas de caractères. En clair tolower est bien pour les chaînes ASCII, mais il ne permet pas de mettre correctement en minuscule une chaîne latin-1 ou utf-8. Vous devez connaître l'encodage de la chaîne et probablement la décoder avant de pouvoir mettre ses caractères en minuscules.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X