web-dev-qa-db-fra.com

Comment convertir std :: string en minuscule?

Je veux convertir un std::string en minuscule. Je suis conscient de la fonction tolower(); cependant, j’ai eu des problèmes avec cette fonction et c’est loin d’être idéal, car son utilisation avec un std::string nécessiterait une itération sur chaque caractère.

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

662
Konrad

De ce :

#include <algorithm>
#include <string> 

std::string data = "Abc"; 
std::transform(data.begin(), data.end(), data.begin(), ::tolower);

Vous n'allez vraiment pas vous échapper en parcourant chaque personnage. Il n'y a aucun moyen de savoir si le caractère est en minuscule ou en majuscule autrement.

Si vous détestez vraiment tolower(), voici une alternative non portable que je ne vous recommande pas d'utiliser:

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

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

Sachez que ::tolower() ne peut effectuer qu'une substitution par caractère sur un octet, ce qui convient mal à de nombreux scripts, en particulier si vous utilisez un codage sur plusieurs octets comme UTF-8.

803
Stefan Mai

Il existe un algorithme de chaîne Boost 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);
291
Rob

Si la chaîne contient des caractères UTF-8 en dehors de la plage ASCII, boost :: algorithm :: to_lower ne les convertira pas. Il vaut mieux utiliser boost :: locale :: to_lower quand UTF-8 est impliqué. Voir http://www.boost.org/doc/libs/1_51_0/libs/locale/doc/html/conversions.html

28
Patrick Ohly

En utilisant une boucle pour C++ 11 basée sur la plage, 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);
}
24
incises

Ceci fait suite à 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. Etant donné que STL stocke les caractères transformés sur l'itérateur de destination (en l'incrémentant à chaque itération de la boucle), la chaîne de destination ne sera pas redimensionnée automatiquement et vous risquez de perdre votre 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;
}
14
user2218467

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;
8
Gilson PJ

Autant que je sache, les bibliothèques Boost ont de très mauvaises performances. J'ai testé leur unordered_map sur STL et la moyenne était 3 fois plus lente (dans le meilleur des cas, 2, le pire était 10 fois). De plus, cet algorithme semble trop bas.

La différence est si grande que je suis persuadé que, quelle que soit l'ajout que vous fassiez pour tolower, le rendre égal à l'augmentation "à vos besoins" sera beaucoup plus rapide que l'augmentation.

J'ai effectué ces tests sur un Amazon EC2, donc les performances ont varié au cours du 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 l'a fait comme ceci:

./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 je vais utiliser cette EC2, je n'ai donc pas vraiment besoin de la tester sur ma machine.

7
Etherealone

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;
}
5
Atul Rokade

std::ctype::tolower() 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";
}
5
Sameer

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

POCO propose deux variantes:

  1. La première variante effectue une copie sans modifier la chaîne d'origine.
  2. La deuxième variante modifie la chaîne d'origine en place.
    Les versions "In Place" ont toujours le nom "InPlace". 

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);
3
Jason Enochs

Il existe un moyen de convertir les majuscules en minuscules SANS faire de tests}, et c'est assez simple. L'utilisation de clocale.h par la fonction/macro isupper () devrait régler les problèmes liés à votre localisation. Sinon, vous pouvez toujours modifier le UtoL [] comme bon vous semble. 

Étant donné que les caractères de C ne sont vraiment que des ints de 8 bits (en ignorant les jeux de caractères larges pour le moment), vous pouvez créer un tableau de 256 octets contenant un jeu de caractères alternatif. 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, attribuez aux membres du tableau en majuscule les valeurs BYTE int pour les caractères en minuscule. Vous pouvez trouver islower () et isupper () utiles ici. 

enter image description here

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 également de remapper tous les autres personnages que vous souhaitez modifier. 

Cette approche présente un avantage considérable lorsqu’elle est exécutée sur des processeurs modernes: il n’est pas nécessaire de faire de prédiction de branche car il n’existe aucun test comprenant une branche. Cela enregistre la logique de prédiction de branche du processeur pour d'autres boucles et tend à éviter les blocages de pipeline. 

Certains ici reconnaîtront peut-être cette approche comme celle utilisée pour convertir EBCDIC en ASCII.

2
user2548100

Voici une technique macro 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 un élément important à prendre en compte, toutefois, si vous travaillez sur quelque chose qui n'est pas simplement ASCII caractères.

2
Volomike

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

Non

Vous devez vous poser plusieurs questions avant de choisir une méthode de minuscule.

  1. Comment la chaîne est-elle encodée? ASCII simple? UTF-8? une certaine forme d'extension ASCII encodage hérité?
  2. Qu'entendez-vous par minuscules? Les règles de mappage des cas varient d'une langue à l'autre! 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 est exécuté? Voulez-vous juste mettre en minuscule ASCII - caractères et passer à travers tout le reste? 
  3. Quelles bibliothèques sont disponibles? 

Une fois que vous avez les réponses à ces questions, vous pouvez commencer à chercher une solution adaptée à vos besoins. Il n'y a pas de solution unique pour tous, partout!

1
plugwash

Etant donné qu'aucune des réponses ne mentionnait la prochaine bibliothèque Ranges, disponible dans la bibliothèque standard depuis C++ 20 et actuellement disponible séparément sur GitHub en tant que range-v3, j'aimerais ajouter un moyen d'effectuer cette conversion .

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 de #include <cctype> et des en-têtes de plages requis.)

Remarque: l’utilisation de unsigned char comme argument du lambda s’inspire de cppreference , qui indique:

Comme toutes les autres fonctions de <cctype>, le comportement de std::tolower n'est pas défini si la valeur de l'argument n'est ni représentable en tant que unsigned char ni égale à EOF. Pour utiliser ces fonctions en toute sécurité avec plain chars (ou signed chars), 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 utilisés directement avec des algorithmes standard lorsque le type de valeur de l'itérateur est char ou signed char. A la place, convertissez d'abord la valeur en unsigned char:

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;
}
1
L. F.
// 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/

1
MoraRockey

Sur les plateformes Microsoft, vous pouvez utiliser la famille de fonctions strlwr: 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 );
}
0
Autodidact

C++ n'a pas implémenté de méthodes tolower ou toupper pour string, mais il est disponible pour char. On peut facilement lire chaque caractère de chaîne, le convertir en casse requise 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 sur une chaîne de caractères: Pour chaque caractère de la chaîne

0
Mahipal

Mon propre modèle fonctionne avec des 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);
}
0
TarmoPikaro

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;
}
0
rashedcs

Utilisez fplus :: to_lower_case ().

(fplus: https://github.com/Dobiasd/FunctionalPlus .

Recherchez 'to_lower_case' dans http://www.editgym.com/fplus-api-search/ )

fplus::to_lower_case(std::string("ABC")) == std::string("abc");
0
Jee lee

Cela pourrait être une autre version simple pour convertir les majuscules en minuscules et vice versa. J'ai utilisé la version de la communauté 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 gérer à l'aide du contrôle de condition.

0
Praveer Kumar

Copier car il a été interdit d'améliorer la réponse. Merci SO


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

Explication:

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

  1. range_declaration: auto& c
    Ici, le spécificateur automatique est utilisé pour la déduction de type automatique. Donc, le type est déduit de l'initialiseur de variables.

  2. range_expression: test
    Dans ce cas, la plage est constituée des caractères de la chaîne test

Les caractères de la chaîne test sont disponibles en tant que référence dans l'identifiant de boucle for c.

0
goulashsoup