Comment convertir std :: string en minuscules?

777

Je veux convertir un std::stringen 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::stringnécessiterait une itération sur chaque caractère.

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

Konrad
la source
34
Sinon, comment convertiriez-vous chaque élément d'une liste de quelque chose en quelque chose d'autre, sans parcourir la liste? Une chaîne n'est qu'une liste de caractères, si vous devez appliquer une fonction à chaque caractère, vous devrez parcourir la chaîne. Pas moyen de contourner ça.
14
Pourquoi exactement cette question fait-elle baisser la note? Je n'ai aucun problème avec l'itération à travers ma chaîne, mais je demande s'il y a d'autres fonctions en dehors de tolower (), toupper () etc.
Konrad
3
Si vous avez un tableau de caractères de style C, je suppose que vous pourrez peut-être ajouter ox20202020 à chaque bloc de 4 caractères (à condition qu'ils soient TOUS déjà en majuscules) pour convertir 4 caractères en minuscules à la fois.
13
@Dan: S'ils peuvent déjà être en minuscules, mais sont définitivement AZ ou az, vous pouvez OU avec 0x20 au lieu d'ajouter. Une de ces optimisations si intelligentes que c'est probablement stupide qui n'en valent presque jamais la peine ...
Steve Jessop
4
Je ne sais pas pourquoi cela aurait été rejeté ... certainement il est formulé un peu bizarrement (parce que vous devez répéter chaque élément d'une manière ou d'une autre), mais c'est une question valable
warren

Réponses:

906

Adapté des questions les moins fréquemment posées :

#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 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:

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()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.

Stefan Mai
la source
25
(Cela peut être vieux, les algorithmes en question ont peu changé) @Stefan Mai: Quel genre de "beaucoup de frais généraux" existe-t-il pour appeler les algorithmes STL? Les fonctions sont plutôt allégées (c'est-à-dire simples pour les boucles) et souvent intégrées car vous avez rarement de nombreux appels à la même fonction avec les mêmes paramètres de modèle dans la même unité de compilation.
eq-
257
Chaque fois que vous supposez que les personnages sont ASCII, Dieu tue un chaton. :(
Brian Gordon
13
Votre premier exemple a potentiellement un comportement indéfini (en passant charà ::tolower(int).) Vous devez vous assurer de ne pas transmettre une valeur négative.
juanchopanza
37
-1 cette utilisation de ::tolowerpourrait bien planter, c'est UB pour une entrée non-ASCII.
Bravo et hth. - Alf
7
Le :: est nécessaire avant tolower pour indiquer qu'il se trouve dans l'espace de noms le plus à l'extérieur. Si vous utilisez ce code dans un autre espace de noms, il peut y avoir une définition différente (éventuellement sans rapport) de tolower qui finirait par être préférentiellement sélectionnée sans le ::.
Charles Ofria
320

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 les non-en place :

#include <boost/algorithm/string.hpp>

const std::string str = "HELLO, WORLD!";
const std::string lower_str = boost::algorithm::to_lower_copy(str);
Rob
la source
2
Je suppose que cela n'a pas les mêmes problèmes que tolower avec une entrée ASCII?
paulm
19
Échoue pour non-ASCII-7.
DevSolar
1
Existe-t-il une version non en place de cela?
Ray
5
@Ray, oui,to_lower_copy
smac89
234

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 personnages 0x7f?)

Si vous utilisez UTF-8 (le seul choix sensé parmi les encodages 8 bits) avec std::stringcomme 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 serait std::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::u16stringet std::u32stringsont 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 :

#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. 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é ...

DevSolar
la source
19
C'est la bonne réponse dans le cas général. La norme ne donne rien pour gérer quoi que ce soit, sauf "ASCII", sauf les mensonges et la tromperie. Cela vous fait penser que vous pouvez peut-être faire face à peut-être UTF-16, mais vous ne pouvez pas. Comme le dit cette réponse, vous ne pouvez pas obtenir la longueur de caractère appropriée (pas la longueur d'octet) d'une chaîne UTF-16 sans effectuer votre propre gestion unicode. Si vous devez gérer du texte réel, utilisez ICU. Merci, @DevSolar
Expiation limitée
ICU est-il disponible par défaut sur Ubuntu / Windows ou doit-il être installé séparément? Et qu'en est-il de cette réponse: stackoverflow.com/a/35075839/207661 ?
Shital Shah
1
Hé, regardez, une vraie réponse! Merci de m'avoir indiqué dans la bonne direction, DevSolar.
Dan Bechard
2
@DevSolar a accepté! Le concept de longueur n'a pas de sens sur le texte (on pourrait ajouter des ligatures à la liste des contrevenants). Cela dit, étant donné que les utilisateurs sont habitués aux tabulations et au contrôle des caractères occupant une unité de longueur, les points de code seraient la mesure la plus intuitive. Oh, et merci d'avoir donné la bonne réponse, triste de le voir si loin :-(
masaers
3
@LF Marginally better. Mais tant de choses ne sont toujours pas couvertes: toupperet tolowerfonctionnent 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.
DevSolar
36

En utilisant la boucle basée sur une plage de C ++ 11, un code plus simple serait:

#include <iostream>       // std::cout
#include <string>         // std::string
#include <locale>         // std::locale, std::tolower

int main ()
{
  std::locale loc;
  std::string str="Test String.\n";

 for(auto elem : str)
    std::cout << std::tolower(elem,loc);
}
incises
la source
9
Cependant, sur une machine française, ce programme ne convertit pas les caractères non ASCII autorisés dans la langue française. Par exemple, une chaîne 'Test String123. É Ï \ n 'sera converti en:' test string123. É Ï \ n 'bien que les caractères É Ï et leurs parties minuscules' é 'et' ï 'soient autorisés en français. Il semble qu'aucune solution n'a été apportée par les autres messages de ce fil.
incise
Je pense que vous devez définir un environnement local approprié pour cela.
user1095108
@incises, alors quelqu'un a posté une réponse sur les soins intensifs et c'est certainement la voie à suivre. Plus facile que la plupart des autres solutions qui tentent de comprendre les paramètres régionaux.
Alexis Wilke
Je préfère ne pas utiliser de bibliothèques externes lorsque cela est possible, personnellement.
kayleeFrye_onDeck
15

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.

#include <string>
#include <algorithm>
#include <iostream>

int main (int argc, char* argv[])
{
  std::string sourceString = "Abc";
  std::string destinationString;

  // Allocate the destination space
  destinationString.resize(sourceString.size());

  // Convert the source string to lower case
  // storing the result in destination string
  std::transform(sourceString.begin(),
                 sourceString.end(),
                 destinationString.begin(),
                 ::tolower);

  // Output the result of the conversion
  std::cout << sourceString
            << " -> "
            << destinationString
            << std::endl;
}
user2218467
la source
1
Cela n'a pas redimensionné Ä en ä pour moi
Purefan
Peut également utiliser un itérateur d'insertion arrière ici au lieu d'un redimensionnement manuel.
chili
11

Une autre approche utilisant une plage basée sur une boucle avec une variable de référence

string test = "Hello World";
for(auto& c : test)
{
   c = tolower(c);
}

cout<<test<<endl;
Gilson PJ
la source
6

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 tolowerle 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.

./test
Elapsed time: 12365milliseconds
Elapsed time: 1640milliseconds
./test
Elapsed time: 26978milliseconds
Elapsed time: 1646milliseconds
./test
Elapsed time: 6957milliseconds
Elapsed time: 1634milliseconds
./test
Elapsed time: 23177milliseconds
Elapsed time: 2421milliseconds
./test
Elapsed time: 17342milliseconds
Elapsed time: 14132milliseconds
./test
Elapsed time: 7355milliseconds
Elapsed time: 1645milliseconds

-O2 fait comme ça:

./test
Elapsed time: 3769milliseconds
Elapsed time: 565milliseconds
./test
Elapsed time: 3815milliseconds
Elapsed time: 565milliseconds
./test
Elapsed time: 3643milliseconds
Elapsed time: 566milliseconds
./test
Elapsed time: 22018milliseconds
Elapsed time: 566milliseconds
./test
Elapsed time: 3845milliseconds
Elapsed time: 569milliseconds

La source:

string str;
bench.start();
for(long long i=0;i<1000000;i++)
{
    str="DSFZKMdskfdsjfsdfJDASFNSDJFXCKVdnjsafnjsdfjdnjasnJDNASFDJDSFSDNJjdsanjfsdnfjJNFSDJFSD";
    boost::algorithm::to_lower(str);
}
bench.end();

bench.start();
for(long long i=0;i<1000000;i++)
{
    str="DSFZKMdskfdsjfsdfJDASFNSDJFXCKVdnjsafnjsdfjdnjasnJDNASFDJDSFSDNJjdsanjfsdnfjJNFSDJFSD";
    for(unsigned short loop=0;loop < str.size();loop++)
    {
        str[loop]=tolower(str[loop]);
    }
}
bench.end();

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.

Etherealone
la source
1
Avez-vous ouvert les options d'optimisation lors de sa compilation? Je pense que la bibliothèque STL heavy boost devrait fonctionner mieux avec un niveau d'optimisation élevé.
Wei Song
1
J'ai utilisé -O2 dans l'un des tests, et rien d'autre.
Etherealone
2
Les performances de unordered_map dépendent de l'algorithme de hachage combiné aux données que vous utilisez. Il n'y a pas d'algorithme de hachage magique qui fonctionne pour toutes les données et pour rendre la carte unordered_map aussi rapide que possible. Comparez et essayez différentes choses. La raison pour laquelle vous obtenez de moins bonnes performances, c'est qu'avec le hachage que vous utilisez, vous obtenez beaucoup de collisions, ce qui provoque essentiellement une recherche dans une liste. Consultez ce site pour plus d'informations: fgda.pl/post/7/gcc-hash-map-vs-unordered-map Pour mes besoins, la fonction fournie sur le lien a réduit les collisions et était donc très rapide.
leetNightshade
6

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

#include <algorithm>
#include <iostream>
#include <string>
using namespace std;
int main(){
    string str;
    getline(cin,str);
//------------function to convert string into lowercase---------------
    transform(str.begin(), str.end(), str.begin(), ::tolower);
//--------------------------------------------------------------------
    cout<<str;
    return 0;
}

2: chaîne sans espaces

#include <algorithm>
#include <iostream>
#include <string>
using namespace std;
int main(){
    string str;
    cin>>str;
//------------function to convert string into lowercase---------------
    transform(str.begin(), str.end(), str.begin(), ::tolower);
//--------------------------------------------------------------------
    cout<<str;
    return 0;
}
Atul Rokade
la source
5

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 tolower

#include <locale>
#include <iostream>

int main () {
  std::locale::global(std::locale("en_US.utf8"));
  std::wcout.imbue(std::locale());
  std::wcout << "In US English UTF-8 locale:\n";
  auto& f = std::use_facet<std::ctype<wchar_t>>(std::locale());
  std::wstring str = L"HELLo, wORLD!";
  std::wcout << "Lowercase form of the string '" << str << "' is ";
  f.tolower(&str[0], &str[0] + str.size());
  std::wcout << "'" << str << "'\n";
}
Sameer
la source
Bien, tant que vous pouvez convertir les caractères en place. Et si votre chaîne source est const? Cela semble le rendre un peu plus compliqué (par exemple, il ne semble pas que vous puissiez l'utiliser f.tolower()), car vous devez mettre les caractères dans une nouvelle chaîne. Souhaitez-vous utiliser transform()et quelque chose comme std::bind1st( std::mem_fun() )pour l'opérateur?
quazar
Pour une chaîne const, nous pouvons simplement faire une copie locale puis la convertir en place.
Sameer
Oui, cependant, faire une copie ajoute plus de frais généraux.
quazar
Vous pouvez utiliser std :: transform avec la version de ctype :: tolower qui ne prend pas de pointeurs. Utilisez un adaptateur d'itérateur d'insertion arrière et vous n'avez même pas à vous soucier de pré-dimensionner votre chaîne de sortie.
chili
Génial, surtout parce que dans libstdc ++ toloweravec localeparamètre, l'appel implicite à use_facetsemble être un goulot d'étranglement des performances. Un de mes collègues a obtenu une augmentation de vitesse de plusieurs 100% en remplaçant boost::iequals(ce qui pose ce problème) par une version qui use_facetn'est appelée qu'une seule fois en dehors de la boucle.
Arne Vogel
3

Une alternative à Boost est POCO (pocoproject.org).

POCO propose deux variantes:

  1. La première variante fait une copie sans altérer la chaîne d'origine.
  2. La deuxième variante modifie la chaîne d'origine en place.
    Les versions "sur place" ont toujours "InPlace" dans le nom.

Les deux versions sont illustrées ci-dessous:

#include "Poco/String.h"
using namespace Poco;

std::string hello("Stack Overflow!");

// Copies "STACK OVERFLOW!" into 'newString' without altering 'hello.'
std::string newString(toUpper(hello));

// Changes newString in-place to read "stack overflow!"
toLowerInPlace(newString);
Jason Enochs
la source
3

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.

entrez la description de l'image ici

Le code ressemble à ceci ...

#include <clocale>
static char UtoL[256];
// ----------------------------------------------------------------------------
void InitUtoLMap()  {
    for (int i = 0; i < sizeof(UtoL); i++)  {
        if (isupper(i)) {
            UtoL[i] = (char)(i + 32);
        }   else    {
            UtoL[i] = i;
        }
    }
}
// ----------------------------------------------------------------------------
char *LowerStr(char *szMyStr) {
    char *p = szMyStr;
    // do conversion in-place so as not to require a destination buffer
    while (*p) {        // szMyStr must be null-terminated
        *p = UtoL[*p];  
        p++;
    }
    return szMyStr;
}
// ----------------------------------------------------------------------------
int main() {
    time_t start;
    char *Lowered, Upper[128];
    InitUtoLMap();
    strcpy(Upper, "Every GOOD boy does FINE!");

    Lowered = LowerStr(Upper);
    return 0;
}

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.

user2548100
la source
2
"Il existe un moyen de convertir les majuscules en minuscules SANS faire si les tests" ont déjà entendu parler de tables de recherche?
Gábor Buella
1
Comportement indéfini pour les caractères négatifs.
Roland Illig
Les processeurs modernes sont goulot d'étranglement en mémoire et non en CPU. L'analyse comparative serait intéressante.
Contango
3

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:

str |= action::transform([](unsigned char c){ return std::tolower(c); });

Pour générer une nouvelle chaîne:

auto new_string = original_string
    | view::transform([](unsigned char c){ return std::tolower(c); });

(N'oubliez pas #include <cctype>et les en-têtes de gammes requis.)

Remarque: l'utilisation de unsigned charcomme argument pour le lambda est inspirée de cppreference , qui stipule:

Comme toutes les autres fonctions de <cctype>, le comportement de std::tolowern'est pas défini si la valeur de l'argument n'est ni représentable unsigned charni égale à EOF. Pour utiliser ces fonctions en toute sécurité avec des chars (ou des signed chars) simples , l'argument doit d'abord être converti en unsigned char:

char my_tolower(char ch)
{
    return static_cast<char>(std::tolower(static_cast<unsigned char>(ch)));
}

De même, ils ne doivent pas être directement utilisés avec des algorithmes standard lorsque le type de valeur de l'itérateur est charou signed char. Au lieu de cela, convertissez la valeur en unsigned charpremier:

std::string str_tolower(std::string s) {
    std::transform(s.begin(), s.end(), s.begin(), 
                // static_cast<int(*)(int)>(std::tolower)         // wrong
                // [](int c){ return std::tolower(c); }           // wrong
                // [](char c){ return std::tolower(c); }          // wrong
                   [](unsigned char c){ return std::tolower(c); } // correct
                  );
    return s;
}
LF
la source
3

Mes propres fonctions de modèle qui exécutent les majuscules / minuscules.

#include <string>
#include <algorithm>

//
//  Lowercases string
//
template <typename T>
std::basic_string<T> lowercase(const std::basic_string<T>& s)
{
    std::basic_string<T> s2 = s;
    std::transform(s2.begin(), s2.end(), s2.begin(), tolower);
    return std::move(s2);
}

//
// Uppercases string
//
template <typename T>
std::basic_string<T> uppercase(const std::basic_string<T>& s)
{
    std::basic_string<T> s2 = s;
    std::transform(s2.begin(), s2.end(), s2.begin(), toupper);
    return std::move(s2);
}
TarmoPikaro
la source
Voilà ce dont j'avais besoin. Je viens d'utiliser le towlowerpour les caractères larges qui prend en charge l'UTF-16.
Juv
2

Voici une macro technique si vous voulez quelque chose de simple:

#define STRTOLOWER(x) std::transform (x.begin(), x.end(), x.begin(), ::tolower)
#define STRTOUPPER(x) std::transform (x.begin(), x.end(), x.begin(), ::toupper)
#define STRTOUCFIRST(x) std::transform (x.begin(), x.begin()+1, x.begin(),  ::toupper); std::transform (x.begin()+1, x.end(),   x.begin()+1,::tolower)

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.

Volomike
la source
1
Je déprécie ceci pour donner des macros lorsqu'une solution parfaitement bonne existe - vous donnez même ces solutions.
Plus clair
2
La technique de macro signifie moins de frappe de code pour quelque chose que l'on utiliserait souvent beaucoup en programmation. Pourquoi ne pas utiliser ça? Sinon, pourquoi avoir des macros?
Volomike
3
Les macros sont un héritage de C sur lequel on travaille dur pour se débarrasser. Si vous souhaitez réduire la quantité de frappe, utilisez une fonction ou un lambda. void strtoupper(std::string& x) { std::transform (x.begin(), x.end(), x.begin(), ::toupper); }
Plus clair
1
@Clearer Comme je veux être un meilleur codeur, pouvez-vous me fournir des liens de documentation ANSI où des comités ANSI C ++ disent quelque chose comme: "Nous devons convoquer une réunion pour se débarrasser des macros de C ++"? Ou une autre feuille de route?
Volomike
2
Non, je ne peux pas. Cependant, la position de Bjarne sur le sujet a été assez claire à plusieurs reprises. En outre, il existe de nombreuses raisons de ne pas utiliser les macros en C ainsi qu'en C ++. xpourrait être une expression valide, qui arrive à se compiler correctement mais donnera des résultats complètement faux à cause des macros.
Plus clair
2
// tolower example (C++)
#include <iostream>       // std::cout
#include <string>         // std::string
#include <locale>         // std::locale, std::tolower

int main ()
{
  std::locale loc;
  std::string str="Test String.\n";
  for (std::string::size_type i=0; i<str.length(); ++i)
    std::cout << std::tolower(str[i],loc);
  return 0;
}

Pour plus d'informations: http://www.cplusplus.com/reference/locale/tolower/

MoraRockey
la source
2

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

Non

Il y a plusieurs questions que vous devez vous poser avant de choisir une méthode de minuscule.

  1. Comment la chaîne est-elle encodée? ASCII ordinaire? UTF-8? une certaine forme de codage hérité ASCII étendu?
  2. Que voulez-vous dire par minuscules de toute façon? Les règles de mappage de cas varient selon les langues! Voulez-vous quelque chose qui est localisé dans les paramètres régionaux des utilisateurs? voulez-vous quelque chose qui se comporte de manière cohérente sur tous les systèmes sur lesquels votre logiciel fonctionne? Voulez-vous simplement mettre en minuscules les caractères ASCII et passer par tout le reste?
  3. Quelles bibliothèques sont disponibles?

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!

plugwash
la source
2

Essayez cette fonction :)

string toLowerCase(string str) {
    int str_len = str.length();
    string final_str = "";
    for(int i=0; i<str_len; i++) {
        char character = str[i];
        if(character>=65 && character<=92) {
            final_str += (character+32);
        } else {
            final_str += character;
        }
    }
    return final_str;
}
BuSaeed
la source
1

Sur les plates-formes Microsoft, vous pouvez utiliser la strlwrfamille de fonctions: http://msdn.microsoft.com/en-us/library/hkxwh33z.aspx

// crt_strlwr.c
// compile with: /W3
// This program uses _strlwr and _strupr to create
// uppercase and lowercase copies of a mixed-case string.
#include <string.h>
#include <stdio.h>

int main( void )
{
   char string[100] = "The String to End All Strings!";
   char * copy1 = _strdup( string ); // make two copies
   char * copy2 = _strdup( string );

   _strlwr( copy1 ); // C4996
   _strupr( copy2 ); // C4996

   printf( "Mixed: %s\n", string );
   printf( "Lower: %s\n", copy1 );
   printf( "Upper: %s\n", copy2 );

   free( copy1 );
   free( copy2 );
}
Autocrate
la source
0

Extrait de code

#include<bits/stdc++.h>
using namespace std;


int main ()
{
    ios::sync_with_stdio(false);

    string str="String Convert\n";

    for(int i=0; i<str.size(); i++)
    {
      str[i] = tolower(str[i]);
    }
    cout<<str<<endl;

    return 0;
}
rashedcs
la source
0

Copiez car il n'a pas été autorisé d'améliorer la réponse. Merci beaucoup


string test = "Hello World";
for(auto& c : test)
{
   c = tolower(c);
}

Explication:

for(auto& c : test)est une boucle for basée sur une plage du type :
for (range_declaration:range_expression)loop_statement

  1. 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.

  2. range_expression: test
    La plage dans ce cas sont les caractères de la chaîne test.

Les caractères de la chaîne testsont disponibles comme référence dans l'identificateur de boucle for c.

goulashsoup
la source
Veuillez préciser d'où vous avez copié votre réponse.
bfontaine
0

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:

#include<iostream>

int main(){
  std::string str = std::string("How IS The Josh");
  for(char &ch : str){
    ch = std::tolower(ch);
  }
  std::cout<<str<<std::endl;
  return 0;
}

Pour une opération basée sur des caractères sur une chaîne: pour chaque caractère de la chaîne

Mahipal
la source
-1

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.

#include <iostream>
#include <string>
using namespace std;

int main()
{
    std::string _input = "lowercasetouppercase";
#if 0
    // My idea is to use the ascii value to convert
    char upperA = 'A';
    char lowerA = 'a';

    cout << (int)upperA << endl; // ASCII value of 'A' -> 65
    cout << (int)lowerA << endl; // ASCII value of 'a' -> 97
    // 97-65 = 32; // Difference of ASCII value of upper and lower a
#endif // 0

    cout << "Input String = " << _input.c_str() << endl;
    for (int i = 0; i < _input.length(); ++i)
    {
        _input[i] -= 32; // To convert lower to upper
#if 0
        _input[i] += 32; // To convert upper to lower
#endif // 0
    }
    cout << "Output String = " << _input.c_str() << endl;

    return 0;
}

Remarque: s'il y a des caractères spéciaux, vous devez les traiter à l'aide de la vérification des conditions.

Praveer Kumar
la source
-8

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é

string LowerCase(string s)
{
    int dif='a'-'A';
    for(int i=0;i<s.length();i++)
    {
        if((s[i]>='A')&&(s[i]<='Z'))
            s[i]+=dif;
    }
   return s;
}

string UpperCase(string s)
{
   int dif='a'-'A';
    for(int i=0;i<s.length();i++)
    {
        if((s[i]>='a')&&(s[i]<='z'))
            s[i]-=dif;
    }
   return s;
}
fdsfdsfdsfds
la source