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?
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.
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);
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
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);
}
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;
}
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;
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.
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;
}
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";
}
Une alternative à Boost est POCO (pocoproject.org).
POCO propose deux variantes:
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);
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.
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.
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.
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.
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!
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 destd::tolower
n'est pas défini si la valeur de l'argument n'est ni représentable en tant queunsigned char
ni égale àEOF
. Pour utiliser ces fonctions en toute sécurité avec plainchar
s (ousigned char
s), l'argument doit d'abord être converti enunsigned 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
ousigned char
. A la place, convertissez d'abord la valeur enunsigned 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; }
// 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/
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 );
}
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
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);
}
#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;
}
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");
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.
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
:
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.
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
.