Convertir une chaîne en C ++ en majuscules


268

Comment pourrait-on convertir une chaîne en majuscules. Les exemples que j'ai trouvés sur Google ne concernent que les caractères.

Réponses:


205

Boost algorithmes de chaîne:

#include <boost/algorithm/string.hpp>
#include <string>

std::string str = "Hello World";

boost::to_upper(str);

std::string newstr = boost::to_upper_copy<std::string>("Hello World");

5
Cela a également l'avantage de i18n, où l' ::toupperon suppose très probablement ASCII.
Ben Straub

4
Votre dernière ligne ne se compile pas - vous devez changer pour quelque chose comme:std::string newstr(boost::to_upper_copy<std::string>("Hello World"));
maxschlepzig

58
cela ne devrait pas être la réponse acceptée car elle nécessite un boost, ou le titre doit être changé.
Andrea

44
oui, je vais installer boost juste pour to_upper ... excellente idée! </sarcasm> :)
thang

12
Je suis personnellement mal disposé à booster comme réponse à "comment faire x en C ++?" parce que boost n'est pas du tout une solution légère. Il semble que vous achetez soit boost en tant que framework (ou ACE ou Qt ou Recusion ToolKit ++ ou ...), soit vous ne le faites pas. Je préfère voir des solutions linguistiques.
jwm

486
#include <algorithm>
#include <string>

std::string str = "Hello World";
std::transform(str.begin(), str.end(),str.begin(), ::toupper);

8
En fait, toupper()peut être implémenté comme une macro. Cela peut provoquer un problème.
dirkgently le

3
un bind (:: toupper, construct <unsigned char> (_ 1)) avec boost.lambda servira parfaitement bien je pense.
Johannes Schaub - litb

11
Cette approche fonctionne bien pour ASCII, mais échoue pour les codages de caractères multi-octets, ou pour les règles de casse spéciales comme l'allemand «ß».
dan04

9
J'ai changé la réponse acceptée en celle utilisant les bibliothèques boost, car elle était plus rapide (dans mes tests informels), plus facile à utiliser et n'a pas les problèmes associés à cette solution. Encore une bonne solution pour les cas où le boost ne peut pas être utilisé.
OrangeAlmondSoap

2
Je ne comprends pas pourquoi le compilateur rejette ce code sans :: qualifier auparavant toupper. Des idées?
sasha.sochka

90

Solution courte utilisant C ++ 11 et toupper ().

for (auto & c: str) c = toupper(c);

Ne serait pas cde const chartype (de auto)? Si c'est le cas, vous ne pouvez pas l'affecter (en raison d'une constpartie) à ce qui est retourné par toupper(c).
PolGraphic

5
@PolGraphic: basé sur une plage, utilise les méthodes begin () / end () du conteneur pour parcourir son contenu. std :: basic_string a à la fois un constateur et un itérateur mutable (retourné respectivement par cbegin () et begin (), voir std :: basic_string :: begin ), donc pour (:) utilise celui approprié (cbegin () si str est déclaré const, avec auto =: = const char, begin () sinon, avec auto =: = char).
Thanasis Papoutsidakis

5
Voir l'anser de dirkgently ci-dessous, cdoit être casté pour unsigned charque cela soit corrigé.
Cris Luengo

to_upper () de boost semble beaucoup plus cohérent avec les fonctions c ++ STL que toupper.
tartaruga_casco_mole

29
struct convert {
   void operator()(char& c) { c = toupper((unsigned char)c); }
};

// ... 
string uc_str;
for_each(uc_str.begin(), uc_str.end(), convert());

Remarque: quelques problèmes avec la meilleure solution:

21.5 Utilitaires de séquence à terminaison nulle

Le contenu de ces en-têtes doit être le même que les en-têtes de la bibliothèque C standard <ctype.h>, <wctype.h>, <string.h>, <wchar.h> et <stdlib.h> [...]

  • Ce qui signifie que les cctypemembres peuvent bien être des macros non adaptées à la consommation directe dans les algorithmes standards.

  • Un autre problème avec le même exemple est qu'il ne jette pas l'argument ni ne vérifie que ce n'est pas négatif; ceci est particulièrement dangereux pour les systèmes où plain charest signé. (La raison en est: si elle est implémentée en tant que macro, elle utilisera probablement une table de recherche et vos arguments dans cette table. Un index négatif vous donnera UB.)


Les membres cctype normaux sont des macros. Je me souviens avoir lu qu'elles devaient également être des fonctions, même si je n'ai pas de copie de la norme C90 et je ne sais pas si cela a été explicitement déclaré ou non.
David Thornley

1
elles doivent être des fonctions en C ++ - même si C leur permet d'être des macros. Je suis d'accord avec votre deuxième point sur le casting. la solution supérieure pourrait passer des valeurs négatives et provoquer UB avec cela. c'est la raison pour laquelle je n'ai pas voté (mais je n'ai pas voté non plus) :)
Johannes Schaub - litb

1
la citation standard ne doit pas manquer: 7.4.2.2/1 (pauvre litb, qui fait référence à un brouillon C99 TC2 uniquement), et C ++ 17.4.1.2/6 dans la norme gloire c ++ 98.
Johannes Schaub - litb

1
(notez la note en bas de page: "Cela interdit la pratique courante de fournir une macro de masquage .... blah blupp .. la seule façon de le faire en C ++ est de fournir une fonction en ligne externe.") :)
Johannes Schaub - litb

1
... qui est atteint par cette ruse: stackoverflow.com/questions/650461/…
Johannes Schaub - litb

27

Ce problème est vectorisable avec SIMD pour le jeu de caractères ASCII.


Comparaisons d'accélération:

Tests préliminaires avec x86-64 gcc 5.2 -O3 -march=nativesur un Core2Duo (Merom). La même chaîne de 120 caractères (ASCII mixte en minuscules et non en minuscules), convertie en boucle 40 millions de fois (sans insertion de fichier croisé, de sorte que le compilateur ne peut pas l'optimiser ou le retirer de la boucle). Mêmes tampons source et dest, donc pas de surcharge malloc ou d'effets mémoire / cache: les données sont chaudes dans le cache L1 tout le temps, et nous sommes purement liés au CPU.

  • boost::to_upper_copy<char*, std::string>(): 198,0 s . Oui, Boost 1.58 sur Ubuntu 15.10 est vraiment aussi lent. J'ai profilé et fait une seule étape dans un débogueur, et c'est vraiment, vraiment mauvais: il y a un dynamic_cast d'une variable locale qui se produit par caractère !!! (dynamic_cast prend plusieurs appels à strcmp). Cela se produit avec LANG=Cet avec LANG=en_CA.UTF-8.

    Je n'ai pas testé en utilisant un RangeT autre que std :: string. Peut-être que l'autre forme d'to_upper_copy optimise mieux, mais je pense que ce sera toujours new/ mallocespace pour la copie, donc c'est plus difficile à tester. Peut-être que quelque chose que j'ai fait diffère d'un cas d'utilisation normal, et peut-être que g ++ est normalement arrêté peut hisser la configuration locale hors de la boucle par caractère. Ma lecture en boucle d'un std::stringet l'écriture vers un char dstbuf[4096]est logique pour les tests.

  • boucle appelant la glibc toupper: 6,67 s (ne vérifie pas le intrésultat pour un potentiel UTF-8 multi-octets, cependant. Cela est important pour le turc.)

  • Boucle ASCII uniquement: 8,79 s (ma version de référence pour les résultats ci-dessous.) Apparemment, une recherche de table est plus rapide qu'une cmov, avec la table chaude en L1 de toute façon.
  • ASCII uniquement auto-vectorisé: 2,51 s . (120 caractères est à mi-chemin entre le pire des cas et le meilleur des cas, voir ci-dessous)
  • ASCII uniquement vectorisé manuellement: 1,35 s

Voir aussi cette question sur la toupper()lenteur sous Windows lorsqu'un paramètre régional est défini .


J'ai été choqué que Boost soit un ordre de grandeur plus lent que les autres options. J'ai revérifié que j'avais -O3activé, et j'ai même fait un pas pour voir ce qu'il faisait. C'est presque exactement la même vitesse avec clang ++ 3.8. Il a une énorme surcharge à l'intérieur de la boucle par caractère. Le résultat perf record/ report(pour l' cyclesévénement perf) est:

  32.87%  flipcase-clang-  libstdc++.so.6.0.21   [.] _ZNK10__cxxabiv121__vmi_class_type_info12__do_dyncastElNS_17__class_type_info10__sub_kindEPKS1_PKvS4_S6_RNS1_16
  21.90%  flipcase-clang-  libstdc++.so.6.0.21   [.] __dynamic_cast                                                                                                 
  16.06%  flipcase-clang-  libc-2.21.so          [.] __GI___strcmp_ssse3                                                                                            
   8.16%  flipcase-clang-  libstdc++.so.6.0.21   [.] _ZSt9use_facetISt5ctypeIcEERKT_RKSt6locale                                                                     
   7.84%  flipcase-clang-  flipcase-clang-boost  [.] _Z16strtoupper_boostPcRKNSt7__cxx1112basic_stringIcSt11char_traitsIcESaIcEEE                                   
   2.20%  flipcase-clang-  libstdc++.so.6.0.21   [.] strcmp@plt                                                                                                     
   2.15%  flipcase-clang-  libstdc++.so.6.0.21   [.] __dynamic_cast@plt                                                                                             
   2.14%  flipcase-clang-  libstdc++.so.6.0.21   [.] _ZNKSt6locale2id5_M_idEv                                                                                       
   2.11%  flipcase-clang-  libstdc++.so.6.0.21   [.] _ZNKSt6locale2id5_M_idEv@plt                                                                                   
   2.08%  flipcase-clang-  libstdc++.so.6.0.21   [.] _ZNKSt5ctypeIcE10do_toupperEc                                                                                  
   2.03%  flipcase-clang-  flipcase-clang-boost  [.] _ZSt9use_facetISt5ctypeIcEERKT_RKSt6locale@plt                                                                 
   0.08% ...

Autovectorisation

Gcc et clang ne vectoriseront automatiquement les boucles que lorsque le nombre d'itérations est connu avant la boucle. (c'est-à-dire que les boucles de recherche comme l'implémentation plain-C de strlenne seront pas autovectorisées.)

Ainsi, pour les chaînes suffisamment petites pour tenir dans le cache, nous obtenons une accélération significative pour les chaînes ~ 128 caractères longtemps avant de strlencommencer. Cela ne sera pas nécessaire pour les chaînes de longueur explicite (comme C ++ std::string).

// char, not int, is essential: otherwise gcc unpacks to vectors of int!  Huge slowdown.
char ascii_toupper_char(char c) {
    return ('a' <= c && c <= 'z') ? c^0x20 : c;    // ^ autovectorizes to PXOR: runs on more ports than paddb
}

// gcc can only auto-vectorize loops when the number of iterations is known before the first iteration.  strlen gives us that
size_t strtoupper_autovec(char *dst, const char *src) {
    size_t len = strlen(src);
    for (size_t i=0 ; i<len ; ++i) {
        dst[i] = ascii_toupper_char(src[i]);  // gcc does the vector range check with psubusb / pcmpeqb instead of pcmpgtb
    }
    return len;
}

Toute libc décente aura une efficacité strlenbeaucoup plus rapide que de boucler un octet à la fois, donc les boucles strlen et toupper vectorisées séparées sont plus rapides.

Ligne de base: une boucle qui vérifie un 0 se terminant à la volée.

Temps pour les itérations 40M, sur un Core2 (Merom) 2,4 GHz. gcc 5.2 -O3 -march=native. (Ubuntu 15.10). dst != src(nous faisons donc une copie), mais ils ne se chevauchent pas (et ne sont pas à proximité). Les deux sont alignés.

  • Chaîne de 15 caractères: ligne de base: 1,08 s. autovec: 1.34s
  • Chaîne de 16 caractères: ligne de base: 1,16 s. autovec: 1,52 s
  • Chaîne de 127 caractères: ligne de base: 8,91 s. autovec: 2.98s // le nettoyage non vectoriel a 15 caractères à traiter
  • Chaîne de 128 caractères: ligne de base: 9,00 s. autovec: 2.06s
  • Chaîne de 129 caractères: ligne de base: 9,04 s. autovec: 2.07s // le nettoyage non vectoriel a 1 caractère à traiter

Certains résultats sont un peu différents avec clang.

La boucle de microbenchmark qui appelle la fonction se trouve dans un fichier séparé. Sinon, il s'aligne et strlen()se hisse hors de la boucle, et il fonctionne considérablement plus vite, esp. pour 16 chaînes de caractères (0,187 s).

Cela a le principal avantage que gcc peut le vectoriser automatiquement pour n'importe quelle architecture, mais l'inconvénient majeur qu'il est plus lent pour le cas généralement courant des petites chaînes.


Il y a donc de grandes accélérations, mais la vectorisation automatique du compilateur ne fait pas un excellent code, en particulier. pour le nettoyage des 15 derniers caractères maximum.

Vectorisation manuelle avec intrinsèque SSE:

Basé sur ma fonction de retournement de casse qui inverse la casse de chaque caractère alphabétique. Il tire parti de "l'astuce de comparaison non signée", où vous pouvez faire low < a && a <= highune seule comparaison non signée par décalage de plage, de sorte que toute valeur inférieure à se lowtermine par une valeur supérieure à high. (Cela fonctionne si lowet highne sont pas trop éloignés.)

SSE a uniquement une comparaison signée supérieure, mais nous pouvons toujours utiliser l'astuce "comparaison non signée" en décalant la plage vers le bas de la plage signée: soustrayez 'a' + 128, de sorte que les caractères alphabétiques varient de -128 à -128 +25 (-128 + 'z' - 'a')

Notez que l'ajout de 128 et la soustraction de 128 sont la même chose pour les entiers 8 bits. Il n'y a nulle part où aller pour le porter, donc c'est juste xor (add sans porter), renversant le bit haut.

#include <immintrin.h>

__m128i upcase_si128(__m128i src) {
    // The above 2 paragraphs were comments here
    __m128i rangeshift = _mm_sub_epi8(src, _mm_set1_epi8('a'+128));
    __m128i nomodify   = _mm_cmpgt_epi8(rangeshift, _mm_set1_epi8(-128 + 25));  // 0:lower case   -1:anything else (upper case or non-alphabetic).  25 = 'z' - 'a'

    __m128i flip  = _mm_andnot_si128(nomodify, _mm_set1_epi8(0x20));            // 0x20:lcase    0:non-lcase

    // just mask the XOR-mask so elements are XORed with 0 instead of 0x20
    return          _mm_xor_si128(src, flip);
    // it's easier to xor with 0x20 or 0 than to AND with ~0x20 or 0xFF
}

Étant donné cette fonction qui fonctionne pour un vecteur, nous pouvons l'appeler en boucle pour traiter une chaîne entière. Puisque nous ciblons déjà SSE2, nous pouvons effectuer une vérification de fin de chaîne vectorisée en même temps.

Nous pouvons également faire beaucoup mieux pour le "nettoyage" des derniers jusqu'à-15 octets restants après avoir fait des vecteurs de 16B: le boîtier supérieur est idempotent, donc le retraitement de certains octets d'entrée est très bien. Nous effectuons un chargement non aligné du dernier 16B de la source et le stockons dans le tampon dest chevauchant le dernier magasin 16B de la boucle.

La seule fois où cela ne fonctionne pas, c'est lorsque la chaîne entière est inférieure à 16B: Même lorsque dst=src, la lecture-modification-écriture non atomique n'est pas la même chose que de ne pas toucher du tout certains octets, et peut casser le code multithread.

Nous avons une boucle scalaire pour cela, et aussi pour nous srcaligner. Puisque nous ne savons pas où se terminera le 0, une charge non alignée srcpourrait traverser la page suivante et segfault. Si nous avons besoin d'octets dans un bloc 16B aligné, il est toujours sûr de charger l'ensemble du bloc 16B aligné.

Source complète: dans un github gist .

// FIXME: doesn't always copy the terminating 0.
// microbenchmarks are for this version of the code (with _mm_store in the loop, instead of storeu, for Merom).
size_t strtoupper_sse2(char *dst, const char *src_begin) {
    const char *src = src_begin;
    // scalar until the src pointer is aligned
    while ( (0xf & (uintptr_t)src) && *src ) {
        *(dst++) = ascii_toupper(*(src++));
    }

    if (!*src)
        return src - src_begin;

    // current position (p) is now 16B-aligned, and we're not at the end
    int zero_positions;
    do {
        __m128i sv = _mm_load_si128( (const __m128i*)src );
        // TODO: SSE4.2 PCMPISTRI or PCMPISTRM version to combine the lower-case and '\0' detection?

        __m128i nullcheck = _mm_cmpeq_epi8(_mm_setzero_si128(), sv);
        zero_positions = _mm_movemask_epi8(nullcheck);
        // TODO: unroll so the null-byte check takes less overhead
        if (zero_positions)
            break;

        __m128i upcased = upcase_si128(sv);   // doing this before the loop break lets gcc realize that the constants are still in registers for the unaligned cleanup version.  But it leads to more wasted insns in the early-out case

        _mm_storeu_si128((__m128i*)dst, upcased);
        //_mm_store_si128((__m128i*)dst, upcased);  // for testing on CPUs where storeu is slow
        src += 16;
        dst += 16;
    } while(1);

    // handle the last few bytes.  Options: scalar loop, masked store, or unaligned 16B.
    // rewriting some bytes beyond the end of the string would be easy,
    // but doing a non-atomic read-modify-write outside of the string is not safe.
    // Upcasing is idempotent, so unaligned potentially-overlapping is a good option.

    unsigned int cleanup_bytes = ffs(zero_positions) - 1;  // excluding the trailing null
    const char* last_byte = src + cleanup_bytes;  // points at the terminating '\0'

    // FIXME: copy the terminating 0 when we end at an aligned vector boundary
    // optionally special-case cleanup_bytes == 15: final aligned vector can be used.
    if (cleanup_bytes > 0) {
        if (last_byte - src_begin >= 16) {
            // if src==dest, this load overlaps with the last store:  store-forwarding stall.  Hopefully OOO execution hides it
            __m128i sv = _mm_loadu_si128( (const __m128i*)(last_byte-15) ); // includes the \0
            _mm_storeu_si128((__m128i*)(dst + cleanup_bytes - 15), upcase_si128(sv));
        } else {
            // whole string less than 16B
            // if this is common, try 64b or even 32b cleanup with movq / movd and upcase_si128
#if 1
            for (unsigned int i = 0 ; i <= cleanup_bytes ; ++i) {
                dst[i] = ascii_toupper(src[i]);
            }
#else
            // gcc stupidly auto-vectorizes this, resulting in huge code bloat, but no measurable slowdown because it never runs
            for (int i = cleanup_bytes - 1 ;  i >= 0 ; --i) {
                dst[i] = ascii_toupper(src[i]);
            }
#endif
        }
    }

    return last_byte - src_begin;
}

Temps pour les itérations 40M, sur un Core2 (Merom) 2,4 GHz. gcc 5.2 -O3 -march=native. (Ubuntu 15.10). dst != src(nous faisons donc une copie), mais ils ne se chevauchent pas (et ne sont pas à proximité). Les deux sont alignés.

  • Chaîne de 15 caractères: ligne de base: 1,08 s. autovec: 1,34 s. manuel: 1,29 s
  • Chaîne de 16 caractères: ligne de base: 1,16 s. autovec: 1,52 s. manuel: 0.335s
  • Chaîne de 31 caractères: manuel: 0,479 s
  • Chaîne de 127 caractères: ligne de base: 8,91 s. autovec: 2,98 s. manuel: 0.925s
  • Chaîne de 128 caractères: ligne de base: 9,00 s. autovec: 2.06s. manuel: 0.931s
  • Chaîne de 129 caractères: ligne de base: 9,04 s. autovec: 2.07s. manuel: 1.02s

(En fait chronométré avec _mm_storedans la boucle, non _mm_storeu, car storeu est plus lent sur Merom même lorsque l'adresse est alignée. C'est bien sur Nehalem et plus tard. J'ai également laissé le code tel quel pour le moment, au lieu de corriger l'échec de la copie le 0 final dans certains cas, parce que je ne veux pas tout re-chronométrer.)

Ainsi, pour les chaînes courtes supérieures à 16B, cela est considérablement plus rapide que la vectorisation automatique. Les longueurs d'une largeur inférieure à un vecteur ne posent pas de problème. Ils peuvent être un problème lors du fonctionnement sur place, en raison d'un blocage de transfert de magasin. (Mais notez qu'il est toujours correct de traiter notre propre sortie, plutôt que l'entrée d'origine, car toupper est idempotent).

Il y a beaucoup de possibilités pour régler cela pour différents cas d'utilisation, selon ce que le code environnant veut et la microarchitecture cible. Il est difficile d'obtenir le compilateur pour émettre du code agréable pour la partie de nettoyage. Utiliser ffs(3)(qui se compile en bsf ou tzcnt sur x86) semble être bon, mais évidemment ce bit a besoin d'être repensé puisque j'ai remarqué un bogue après avoir écrit la plupart de cette réponse (voir les commentaires FIXME).

Des accélérations vectorielles pour des chaînes encore plus petites peuvent être obtenues avec movqou des movdcharges / stockages. Personnalisez au besoin pour votre cas d'utilisation.


UTF-8:

Nous pouvons détecter quand notre vecteur a des octets avec le bit le plus élevé et, dans ce cas, revenir à une boucle scalaire prenant en charge utf-8 pour ce vecteur. Le dstpoint peut avancer d'une quantité différente de celle du srcpointeur, mais une fois que nous revenons à un srcpointeur aligné , nous ne faisons que stocker des vecteurs non alignés dst.

Pour le texte qui est UTF-8, mais qui se compose principalement du sous-ensemble ASCII d'UTF-8, cela peut être bon: hautes performances dans le cas commun avec un comportement correct dans tous les cas. Quand il y a beaucoup de non-ASCII, ce sera probablement pire que de rester dans la boucle consciente scalaire UTF-8, cependant.

Rendre l'anglais plus rapide au détriment des autres langues n'est pas une décision d'avenir si l'inconvénient est significatif.


Sensible aux paramètres régionaux:

Dans les paramètres régionaux turcs ( tr_TR), le résultat correct toupper('i')est 'İ'(U0130) et non 'I'(ASCII ordinaire). Voir les commentaires de Martin Bonner sur une question concernant la tolower()lenteur sous Windows.

Nous pouvons également rechercher une liste d'exceptions et un retour à scalaire, comme pour les caractères d'entrée UTF8 multi-octets.

Avec une telle complexité, SSE4.2 PCMPISTRMou quelque chose pourrait être capable de faire beaucoup de nos vérifications en une seule fois.


20

Avez-vous des caractères ASCII ou internationaux dans les chaînes?

Si c'est le dernier cas, "majuscule" n'est pas aussi simple que cela, et cela dépend de l'alphabet utilisé. Il existe des alphabets bicaméraux et unicaméraux. Seuls les alphabets bicaméraux ont des caractères différents pour les majuscules et les minuscules. En outre, il existe des caractères composites, comme la lettre majuscule latine 'DZ' (\ u01F1 'DZ') qui utilisent ce qu'on appelle la casse du titre . Cela signifie que seul le premier caractère (D) est modifié.

Je vous suggère de vous pencher sur l' ICU et de faire la différence entre les mappages de cas simples et complets. Cela pourrait aider:

http://userguide.icu-project.org/transforms/casemappings


7
Ou l'eszet allemand (sp?), La chose qui ressemble à la lettre grecque bêta, et signifie "ss". Il n'y a pas un seul caractère allemand qui signifie "SS", qui est l'équivalent en majuscules. Le mot allemand pour "rue", lorsqu'il est en majuscule, obtient un caractère de plus.
David Thornley

6
Un autre cas particulier est la lettre grecque sigma (Σ), qui a deux versions en minuscules, selon qu'elle se trouve à la fin d'un mot (ς) ou non (σ). Et puis il y a des règles spécifiques à la langue, comme le turc ayant le mappage de cas I↔ı et İ↔i.
dan04

1
Le "majuscule" est appelé pliage du boîtier.
Columbo

20
string StringToUpper(string strToConvert)
{
   for (std::string::iterator p = strToConvert.begin(); strToConvert.end() != p; ++p)
       *p = toupper(*p);

   return p;
}

Ou,

string StringToUpper(string strToConvert)
{
    std::transform(strToConvert.begin(), strToConvert.end(), strToConvert.begin(), ::toupper);

    return strToConvert;
}

4
si vous n'avez pas accès au boost, la deuxième solution est probablement la meilleure que vous puissiez obtenir. que font les étoiles **après les paramètres de la première solution?
Sam Brinck

1
Je suis presque sûr que **c'est une faute de frappe d'essayer d'utiliser une police en gras dans la syntaxe du code.
MasterHD

1
Ce code invoque un comportement non défini lorsqu'il toupperest appelé avec des nombres négatifs.
Roland Illig

17

Ce qui suit fonctionne pour moi.

#include <algorithm>
void  toUpperCase(std::string& str)
{
    std::transform(str.begin(), str.end(), str.begin(), ::toupper);
}

int main()
{
   std::string str = "hello";
   toUpperCase(&str);
}

Notez que std :: transform est défini dans <algorithm>
edj

Oui. ce # include est requis, #include <algorithm>
Pabitra Dash

1
Ce code invoque un comportement non défini lorsqu'il toupperest appelé avec des nombres négatifs.
Roland Illig

duplicata de la réponse donnée par l'utilisateur648545 - -1
Piotr Dobrogost

@PiotrDobrogost Je n'ai aucune idée de la réponse donnée par user648545. Je n'ai pas copié cela. Lorsque je compare deux méthodes, la signature de la méthode est complètement différente, bien que les deux fonctions utilisent la fonction de bibliothèque transformée.
Pabitra Dash

13

Utilisez un lambda.

std::string s("change my case");

auto to_upper = [] (char_t ch) { return std::use_facet<std::ctype<char_t>>(std::locale()).toupper(ch); };

std::transform(s.begin(), s.end(), s.begin(), to_upper);

2
Byron, ne vous inquiétez pas des autres commentaires. Il est tout à fait correct de répondre aux anciennes questions avec une nouvelle solution (moderne) comme vous l'avez fait.
Kyberias

13

Le plus rapide si vous n'utilisez que des caractères ASCII :

for(i=0;str[i]!=0;i++)
  if(str[i]<='z' && str[i]>='a')
    str[i]-=32;

Veuillez noter que ce code s'exécute plus rapidement mais ne fonctionne que sur ASCII et n'est pas une solution "abstraite".

Si vous avez besoin de solutions UNICODE ou de solutions plus conventionnelles et abstraites, optez pour d'autres réponses et travaillez avec des méthodes de chaînes C ++.


1
La question est étiquetée comme C++, mais vous avez écrit une Créponse ici. (Je ne suis pas un des downvoters.)
hkBattousai

6
J'ai écrit une réponse C ET une réponse C ++ ici parce que C ++ est écrit pour être entièrement compatible avec les sources C, donc toute solution C est également une solution correcte C ++
Luca C.

Mais il est tellement préférable de donner une réponse qui respecte la manière C ++.
Dmitriy Yurchenko

La manière standard de c ++ serait d'utiliser std :: transform avec toupper. C'est moins de code et certainement portable. Ce code repose sur le "fait" que le système utilisera ascii comme mécanisme de codage des caractères. Pas sûr que tous les systèmes soient basés sur cet encodage et donc pas sûr que ce soit portable.
AlexTheo

1
Pourquoi avez-vous décidé d'utiliser des codes ASCII au lieu des caractères inclus '?
HolyBlackCat

11

Tant que vous êtes bien avec ASCII uniquement et que vous pouvez fournir un pointeur valide vers la mémoire RW, il existe une ligne simple et très efficace en C:

void strtoupper(char* str)
{ 
    while (*str) *(str++) = toupper((unsigned char)*str);
}

C'est particulièrement bon pour les chaînes simples comme les identifiants ASCII que vous souhaitez normaliser dans la même casse de caractères. Vous pouvez ensuite utiliser le tampon pour construire une instance std: string.


On note que cette réponse est pour une chaîne ca plutôt qu'une chaîne std ::
EvilTeach

Cela présente une faille de sécurité inhérente évidente. Je ne ferais pas ça.
Byron

9
//works for ASCII -- no clear advantage over what is already posted...

std::string toupper(const std::string & s)
{
    std::string ret(s.size(), char());
    for(unsigned int i = 0; i < s.size(); ++i)
        ret[i] = (s[i] <= 'z' && s[i] >= 'a') ? s[i]-('a'-'A') : s[i];
    return ret;
}

s.size () est de type std :: size_t qui, AFAIK pourrait très bien ne pas être signé en fonction de l'implémentation
odinthenerd

Je ne pense pas qu'il existe des implémentations modernes dans lesquelles le résultat de std :: string :: size est signé. Étant donné que, à la fois sémantiquement et pratiquement, il n'y a pas de taille négative, je vais aller avec size_t étant au moins un entier non signé 32 bits.
user1329482

Il n'y a aucune raison de ne pas écrire for (size_t i = 0 .... Il n'y a également aucune bonne raison de le rendre si difficile à lire. Cela copie également la chaîne en premier puis la boucle. @ La réponse de Luke est meilleure à certains égards, sauf pour ne pas profiter des 'a'constantes de caractère.
Peter Cordes

9
#include <string>
#include <locale>

std::string str = "Hello World!";
auto & f = std::use_facet<std::ctype<char>>(std::locale());
f.toupper(str.data(), str.data() + str.size());

Cela fonctionnera mieux que toutes les réponses qui utilisent la fonction globale toupper, et c'est probablement ce que boost :: to_upper fait en dessous.

C'est parce que :: toupper doit rechercher les paramètres régionaux - car ils peuvent avoir été modifiés par un thread différent - pour chaque appel, alors qu'ici seul l'appel à locale () a cette pénalité. Et rechercher les paramètres régionaux implique généralement de prendre un verrou.

Cela fonctionne également avec C ++ 98 après avoir remplacé l'auto, l'utilisation de la nouvelle str.data () non const et ajouté un espace pour interrompre la fermeture du modèle (">>" à ">>") comme ceci:

std::use_facet<std::ctype<char> > & f = 
    std::use_facet<std::ctype<char> >(std::locale());
f.toupper(const_cast<char *>(str.data()), str.data() + str.size());

7
typedef std::string::value_type char_t;

char_t up_char( char_t ch )
{
    return std::use_facet< std::ctype< char_t > >( std::locale() ).toupper( ch );
}

std::string toupper( const std::string &src )
{
    std::string result;
    std::transform( src.begin(), src.end(), std::back_inserter( result ), up_char );
    return result;
}

const std::string src  = "test test TEST";

std::cout << toupper( src );

Je ne recommanderais pas un back_inserter car vous connaissez déjà la longueur; utilisez std :: string result (src.size ()); std :: transform (src.begin (), src.end (), result.begin (), up_char);
Viktor Sehr

Je suis sûr que vous le savez.
Viktor Sehr

@Viktor Sehr, @bayda: Je sais que cela a 2 ans, mais pourquoi ne pas tirer le meilleur parti des deux mondes. Utilisez reserveet back_inserter(pour que la chaîne ne soit copiée qu'une seule fois). inline std::string to_lower(const std::string &s) { std::string result; result.reserve(s.size()); std::transform(s.begin(), s.end(), std::back_inserter( result ), static_cast<int(*)(int)>(std::tolower)); return result; }
Evan Teran

4
std::string value;
for (std::string::iterator p = value.begin(); value.end() != p; ++p)
    *p = toupper(*p);

Ce code invoque un comportement non défini lorsqu'il toupperest appelé avec des nombres négatifs.
Roland Illig

2

essayez la toupper()fonction ( #include <ctype.h>). il accepte les caractères comme arguments, les chaînes sont composées de caractères, vous devrez donc parcourir chaque caractère individuel qui, une fois mis en place, comprend la chaîne


Cette suggestion invoque un comportement indéfini lorsqu'elle toupperest appelée avec des nombres négatifs. Vous auriez dû mentionner le casting nécessaire pour unsigned char.
Roland Illig

2

Voici le dernier code avec C ++ 11

std::string cmd = "Hello World";
for_each(cmd.begin(), cmd.end(), [](char& in){ in = ::toupper(in); });

Ce code invoque un comportement non défini lorsqu'il toupperest appelé avec des nombres négatifs.
Roland Illig

1

Utilisation de Boost.Text, qui fonctionnera pour le texte Unicode

boost::text::text t = "Hello World";
boost::text::text uppered;
boost::text::to_title(t, std::inserter(uppered, uppered.end()));
std::string newstr = uppered.extract();

1

La réponse de @dirkgently est très inspirante, mais je tiens à souligner qu'en raison de l'inquiétude indiquée ci-dessous,

Comme toutes les autres fonctions de, le comportement de std :: toupper n'est pas défini si la valeur de l'argument n'est ni représentable comme caractère non signé ni égale à EOF. Pour utiliser ces fonctions en toute sécurité avec des caractères simples (ou des caractères signés), l'argument doit d'abord être converti en caractères non signés
Référence : std :: toupper

l'utilisation correcte de std::toupperdevrait être:

#include <algorithm>
#include <cctype>
#include <iostream>
#include <iterator>
#include <string>

void ToUpper(std::string& input)
{
    std::for_each(std::begin(input), std::end(input), [](char& c) {
        c = static_cast<char>(std::toupper(static_cast<unsigned char>(c)));
    });
}

int main()
{
    std::string s{ "Hello world!" };
    std::cout << s << std::endl;
    ::ToUpper(s);
    std::cout << s << std::endl;

    return 0;
}

Production:

Hello world!
HELLO WORLD!

0

pas sûr qu'il y ait une fonction intégrée. Essaye ça:

Incluez les bibliothèques ctype.h OU cctype, ainsi que stdlib.h dans le cadre des directives du préprocesseur.

string StringToUpper(string strToConvert)
{//change each element of the string to upper case
   for(unsigned int i=0;i<strToConvert.length();i++)
   {
      strToConvert[i] = toupper(strToConvert[i]);
   }
   return strToConvert;//return the converted string
}

string StringToLower(string strToConvert)
{//change each element of the string to lower case
   for(unsigned int i=0;i<strToConvert.length();i++)
   {
      strToConvert[i] = tolower(strToConvert[i]);
   }
   return strToConvert;//return the converted string
}

.length () n'est pas de type 'unsigned int'
malat

Ce code invoque un comportement non défini lorsqu'il toupperest appelé avec des nombres négatifs.
Roland Illig

0

Ma solution (effacer le 6e bit pour alpha):

#include <ctype.h>

inline void toupper(char* str)
{
    while (str[i]) {
        if (islower(str[i]))
            str[i] &= ~32; // Clear bit 6 as it is what differs (32) between Upper and Lowercases
        i++;
    }
}

Ce code invoque un comportement non défini lorsqu'il toupperest appelé avec des nombres négatifs.
Roland Illig

Non ... Veuillez vérifier que vous avez raison avant de voter. Islower ne travaillerait que sur des valeurs non négatives ...
Antonin GAVREL

-1

TOUTES ces solutions sur cette page sont plus difficiles qu'elles ne devraient l'être.

Faites ceci

RegName = "SomE StRing That you wAnt ConvErTed";
NameLength = RegName.Size();
for (int forLoop = 0; forLoop < NameLength; ++forLoop)
{
     RegName[forLoop] = tolower(RegName[forLoop]);
}

RegNameest votre string. Obtenez votre taille de chaîne ne pas utiliser string.size()comme testeur réel, très désordonné et peut causer des problèmes. puis. la forboucle la plus basique .

rappelez-vous que la taille de la chaîne renvoie également le délimiteur, utilisez donc <et non <= dans votre test de boucle.

la sortie sera: une chaîne que vous souhaitez convertir


4
Je ne vois pas comment c'est plus simple que la solution boost :: toupper. Peux-tu élaborer?
tr9sh

2
Il existe déjà de nombreuses tolowerboucles simples , et la plupart d'entre elles utilisent des noms de variables de boucle standard comme i, pas les étranges forLoop.
Peter Cordes

-1

Sans utiliser de bibliothèques:

std::string YourClass::Uppercase(const std::string & Text)
{
    std::string UppperCaseString;
    UppperCaseString.reserve(Text.size());
    for (std::string::const_iterator it=Text.begin(); it<Text.end(); ++it)
    {
        UppperCaseString.push_back(((0x60 < *it) && (*it < 0x7B)) ? (*it - static_cast<char>(0x20)) : *it);
    }
    return UppperCaseString;
}

Le code ci-dessus fonctionne uniquement pour les encodages compatibles ASCII. Ni la question ni la réponse ne mentionnent cette restriction. L'un d'eux devrait.
Roland Illig

-1

Si vous ne vous préoccupez que des caractères 8 bits (ce que toutes les autres réponses sauf Milan Babuškov supposent également), vous pouvez obtenir la vitesse la plus rapide en générant une table de recherche au moment de la compilation à l'aide de la métaprogrammation. Sur ideone.com, cela fonctionne 7 fois plus vite que la fonction de bibliothèque et 3 fois plus vite qu'une version manuscrite ( http://ideone.com/sb1Rup ). Il est également personnalisable grâce à des traits sans ralentissement.

template<int ...Is>
struct IntVector{
using Type = IntVector<Is...>;
};

template<typename T_Vector, int I_New>
struct PushFront;
template<int ...Is, int I_New>
struct PushFront<IntVector<Is...>,I_New> : IntVector<I_New,Is...>{};

template<int I_Size, typename T_Vector = IntVector<>>
struct Iota : Iota< I_Size-1, typename PushFront<T_Vector,I_Size-1>::Type> {};
template<typename T_Vector>
struct Iota<0,T_Vector> : T_Vector{};

template<char C_In>
struct ToUpperTraits {
    enum { value = (C_In >= 'a' && C_In <='z') ? C_In - ('a'-'A'):C_In };
};

template<typename T>
struct TableToUpper;
template<int ...Is>
struct TableToUpper<IntVector<Is...>>{
    static char at(const char in){
        static const char table[] = {ToUpperTraits<Is>::value...};
        return table[in];
    }
};

int tableToUpper(const char c){
    using Table = TableToUpper<typename Iota<256>::Type>;
    return Table::at(c);
}

avec cas d'utilisation:

std::transform(in.begin(),in.end(),out.begin(),tableToUpper);

Pour une description en profondeur (plusieurs pages) de son fonctionnement, permettez-moi de brancher sans vergogne mon blog: http://metaporky.blogspot.de/2014/07/part-4-generating-look-up-tables-at.html


-1
template<size_t size>
char* toupper(char (&dst)[size], const char* src) {
    // generate mapping table once
    static char maptable[256];
    static bool mapped;
    if (!mapped) {
        for (char c = 0; c < 256; c++) {
            if (c >= 'a' && c <= 'z')
                maptable[c] = c & 0xdf;
            else
                maptable[c] = c;
        }
        mapped = true;
    }

    // use mapping table to quickly transform text
    for (int i = 0; *src && i < size; i++) {
        dst[i] = maptable[*(src++)];
    }
    return dst;
}

-1

Cette fonction c ++ renvoie toujours la chaîne majuscule ...

#include <locale> 
#include <string>
using namespace std; 
string toUpper (string str){
    locale loc; 
    string n; 
    for (string::size_type i=0; i<str.length(); ++i)
        n += toupper(str[i], loc);
    return n;
}

-3

J'utilise cette solution. Je sais que vous n'êtes pas censé modifier cette zone de données .... mais je pense que c'est principalement pour les bogues de dépassement de tampon et le caractère nul .... les choses majuscules ne sont pas les mêmes.

void to_upper(const std::string str) {
    std::string::iterator it;
    int i;
    for ( i=0;i<str.size();++i ) {
        ((char *)(void *)str.data())[i]=toupper(((char *)str.data())[i]);
    }
}

I know you're not supposed to modify that data area- quelle zone de données n'êtes-vous pas censé modifier?
user93353

3
C'est tard, mais que diable? Cette ligne folle peut être remplacée par str[i] = toupper(str[i]);parfaitement fine (enfin, pas parfaitement fine, mais elle corrige la plupart des problèmes).
chris
En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.