Question Comment convertir std :: string en minuscules?


Je veux convertir un std::string en minuscules. Je suis conscient de la fonction tolower(), mais dans le passé, j'ai eu des problèmes avec cette fonction et il n'est guère idéal de toute façon que l'utilisation d'un std::string nécessiterait d'itérer sur chaque personnage.

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


620
2017-11-24 11:49


origine


Réponses:


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 en passer à travers chaque personnage. Il n'y a aucun moyen de savoir si le caractère est minuscule ou majuscule sinon.

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);

Soit conscient que ::tolower() ne peut effectuer qu'une substitution par caractère à un seul octet, ce qui est mal adapté pour de nombreux scripts, en particulier si vous utilisez un codage multi-octets comme UTF-8.


749
2017-11-24 11:59



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 un non-lieu:

#include <boost/algorithm/string.hpp>    

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

281
2017-11-24 11:57



tl; dr

Utilisez le Bibliothèque ICU.


Vous devez d'abord répondre à une question: Quelle est la codage 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-elle misérablement pour les personnages 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à en vous faisant croire 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 conscient du concept multi-octets. Même quelque chose d'aussi simple que .substr() est une bombe à retardement. (Parce que la division 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 tout encodage, vous êtes en difficulté profonde. (Parce qu'il n'est tout simplement pas possible de faire cela "correctement" avec la bibliothèque standard, qui ne peut que un caractère de résultat, pas le "SS" nécessaire ici.) [1] Un autre exemple serait std::tolower( 'I' ), ce qui devrait donner des résultats différents en fonction des paramètres régionaux. En Allemagne, 'i' serait correct; en Turquie, 'ı' (LETTRE MINUSCULE LATINE DOTLESS I) est le résultat attendu.

Ensuite, il y a le point que la bibliothèque standard dépend des paramètres régionaux prise en charge sur la machine sur laquelle tourne votre logiciel ... et que faites-vous si ce n'est pas le cas?

Alors qu'est-ce que tu es vraiment la recherche d'une classe de chaînes capable de gérer correctement tout cela, et c'est ne pas  std::string.

(Note C ++ 11: std::u16string et std::u32string sont meilleur, mais toujours pas parfait.)

Alors que Boost regards Nice, API sage, Boost.Locale est fondamentalement une enveloppe autour ICU. Si Boost est compilé avec le support ICU ... si ce n'est pas le cas, Boost.Locale est limité à la prise en charge des paramètres régionaux compilée pour la bibliothèque standard.

Et crois 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, vous devez donc les fournir avec votre application, et cette ouvre une toute nouvelle boîte de vers ...)

Donc, personnellement, je recommande d'obtenir le support Unicode complet de la bouche du cheval et en utilisant le ICU bibliothèque directement:

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

#include <iostream>

int main()
{
    char const * someString = "Eidenges\xe4\xdf";
    icu::UnicodeString someUString( someString, "ISO-8859-1" );
    // Setting the locale explicitly here for completeness.
    // Usually you would use the user-specified system locale.
    std::cout << someUString.toLower( "de_DE" ) << "\n";
    std::cout << someUString.toUpper( "de_DE" ) << "\n";
    return 0;
}

Compiler (avec G ++ dans cet exemple):

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

Cela donne:

eidengesäß
EIDENGESÄSS

[1] En 2017, le Conseil d'Orthographe Allemand a décidé que "ẞ" U + 1E9E LA LETTRE MAJUSCULE LATINE SHARP S pourrait être utilisée officiellement, en tant qu'option à côté de la conversion traditionnelle "SS" pour éviter toute ambiguïté, par ex. dans les passeports (où les noms sont capitalisés). Mon bel exemple, rendu obsolète par décision du comité ...


167
2018-06-05 15:06



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 vaut 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


27
2017-10-10 07:24



À l'aide de la plage de 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);
}

22
2017-10-09 08:00



Ceci est une 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. Comme les mémoires STL transforment les caractères à 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 perdre de 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;
}

14
2018-03-28 06:25



Autant que je vois les bibliothèques de Boost sont vraiment mauvaises performances. J'ai testé leur unordered_map à STL et il était en moyenne 3 fois plus lent (le meilleur cas 2, le pire était 10 fois). En outre, cet algorithme semble trop faible.

La différence est si grande que je suis sûr que tout ce que vous devrez faire pour tolower pour le rendre égal à stimuler "pour vos besoins" sera bien plus vite que stimuler.

J'ai fait ces tests sur un Amazon EC2, donc les performances ont varié pendant le test mais vous avez quand même 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 donc je n'ai pas vraiment besoin de la tester sur ma machine.


7
2017-08-04 20:01



std::ctype::tolower() à partir de la bibliothèque de localisation C ++ standard le fera correctement pour vous. Voici un exemple extrait du tolower page de référence

#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
2018-01-29 02:25



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

4
2018-06-12 06:50



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;

4
2018-01-10 19:53