Come convertire std :: string in lettere minuscole?


777

Voglio convertire un std::stringin minuscolo. Sono a conoscenza della funzione tolower(), tuttavia in passato ho avuto problemi con questa funzione e non è certo l'ideale in quanto l'uso con un std::stringrichiede l'iterazione su ogni personaggio.

Esiste un'alternativa che funziona il 100% delle volte?


34
In quale altro modo convertiresti ogni elemento di un elenco di qualcosa in qualcos'altro, senza scorrere l'elenco? Una stringa è solo un elenco di caratteri, se è necessario applicare una funzione a ciascun carattere, sarà necessario scorrere la stringa. Assolutamente no.

14
Perché esattamente questa domanda riduce il rating? Non ho problemi con l'iterazione attraverso la mia stringa, ma chiedo se ci sono altre funzioni oltre a tolower (), toupper () ecc.
Konrad

3
Se hai un array di caratteri in stile C, suppongo che potresti essere in grado di aggiungere ox20202020 a ciascun blocco di 4 caratteri (a condizione che siano TUTTI già in maiuscolo) per convertire 4 caratteri in minuscolo alla volta.

13
@Dan: se potrebbero essere già in minuscolo, ma sono sicuramente AZ o az, puoi OR con 0x20 invece di aggiungere. Una di quelle ottimizzazioni così intelligenti, probabilmente stupide che non valgono quasi mai la pena ...
Steve Jessop,

4
Non so perché sarebbe stato votato verso il basso ... sicuramente è formulato in modo un po 'strano (perché in qualche modo devi scorrere ogni elemento), ma è una domanda valida
warren

Risposte:


906

Adattato da domande non così frequenti :

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

Non riuscirai davvero a scappare senza ripetere ogni personaggio. Non c'è modo di sapere se il personaggio è minuscolo o maiuscolo altrimenti.

Se davvero odi tolower(), ecco un'alternativa specializzata solo per ASCII che non ti consiglio di usare:

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

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

Tenere presente che è tolower()possibile eseguire solo una sostituzione per singolo byte di caratteri, che non è adatta per molti script, soprattutto se si utilizza una codifica multibyte come UTF-8.


25
(Vecchio potrebbe essere, gli algoritmi in questione sono cambiati poco) @Stefan Mai: Che tipo di "intero overhead" c'è nel chiamare algoritmi STL? Le funzioni sono piuttosto snelle (cioè semplici per i loop) e spesso sottolineate poiché raramente si hanno molte chiamate alla stessa funzione con gli stessi parametri del modello nella stessa unità di compilazione.
eq-

257
Ogni volta che supponi che i personaggi siano ASCII, Dio uccide un gattino. :(
Brian Gordon,

13
Il tuo primo esempio potenzialmente ha un comportamento indefinito (passando chara ::tolower(int).) Devi assicurarti di non passare un valore negativo.
juanchopanza,

37
-1 questo uso di ::tolowerpotrebbe andare in crash, è UB per input non ASCII.
Saluti e hth. - Alf

7
:: è necessario prima di tolower per indicare che si trova nello spazio dei nomi più esterno. Se si utilizza questo codice in un altro spazio dei nomi, potrebbe esserci una diversa (possibilmente non correlata) definizione di tolower che finirebbe per essere selezionata preferenzialmente senza il ::.
Charles Ofria,

320

Boost fornisce un algoritmo di stringa per questo :

#include <boost/algorithm/string.hpp>

std::string str = "HELLO, WORLD!";
boost::algorithm::to_lower(str); // modifies str

Oppure, per non sul posto :

#include <boost/algorithm/string.hpp>

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

2
Presumo che questo non abbia gli stessi problemi di tolower con input ASCII?
paulm

19
Errore per non-ASCII-7.
DevSolar,

1
Esiste una versione non sul posto di questo?
Ray,

5
@Ray, yes,to_lower_copy
smac89

234

tl; dr

Utilizzare la libreria ICU . In caso contrario, la routine di conversione si interromperà silenziosamente in casi che probabilmente non si conoscono.


Per prima cosa devi rispondere a una domanda: qual è la tua codificastd::string ? È ISO-8859-1? O forse ISO-8859-8? O Windows Codepage 1252? Qualunque cosa tu stia usando per convertire maiuscole in minuscole lo sa? (O fallisce miseramente per i personaggi finiti 0x7f?)

Se stai usando UTF-8 (l'unica scelta sana tra le codifiche a 8 bit) con std::stringcome contenitore, stai già ingannando te stesso nel credere di avere ancora il controllo delle cose, perché stai memorizzando una sequenza di caratteri multibyte in un contenitore che non è a conoscenza del concetto multibyte. Anche qualcosa di semplice come .substr()una bomba a orologeria. (Perché la divisione di una sequenza multibyte comporterà una stringa (sotto) non valida.)

E non appena provi qualcosa del genere std::toupper( 'ß' ), in qualsiasi codifica, sei in guai seri. (Perché semplicemente non è possibile fare questo "giusto" con la libreria standard, che può fornire solo un carattere risultato, non il "SS"necessario qui.) [1] Un altro esempio potrebbe essere std::tolower( 'I' ), che dovrebbe produrre risultati diversi a seconda delle impostazioni locali . In Germania, 'i'sarebbe corretto; in Turchia, 'ı'(LATIN SMALL LOTTER DOTLESS I) è il risultato atteso (che, di nuovo, è più di un byte nella codifica UTF-8). Ancora un altro esempio è il greco Sigma , maiuscolo '∑', minuscolo 'σ'... tranne alla fine di una parola, dove si trova 'ς'.

Pertanto, qualsiasi conversione di caso che funziona su un personaggio alla volta, o peggio, su un byte alla volta, viene interrotta in base alla progettazione.

Quindi c'è il punto che la libreria standard, per quello che è in grado di fare, dipende da quali impostazioni locali sono supportate sulla macchina su cui è in esecuzione il software ... e cosa fai se non lo è?

Quindi quello che stai veramente cercando è una classe di stringhe in grado di gestire correttamente tutto ciò e che non sia una delle std::basic_string<>varianti .

(Nota C ++ 11: std::u16stringe std::u32stringsono migliori , ma ancora non perfetti. C ++ 20 ha portato std::u8string, ma tutto ciò che fa è specificare la codifica. Per molti altri aspetti rimangono ancora ignoranti della meccanica Unicode, come la normalizzazione, le regole di confronto, .. .)

Mentre Boost sembra bello, per quanto riguarda le API, Boost.Locale è fondamentalmente un wrapper per ICU . Se Boost è compilato con il supporto ICU ... in caso contrario, Boost.Locale è limitato al supporto locale compilato per la libreria standard.

E mi creda, ottenendo Boost di compilare con terapia intensiva può essere un vero e proprio dolore a volte. (Non ci sono file binari precompilati per Windows, quindi dovresti fornirli insieme alla tua applicazione e questo aprirà una nuova lattina di worm ...)

Quindi personalmente consiglierei di ottenere il pieno supporto Unicode direttamente dalla bocca del cavallo e di usare direttamente la libreria 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;
}

Compila (con G ++ in questo esempio):

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

Questo da:

ὀδυσσεύς

Nota che la conversione Σ <-> σ nel mezzo della parola e la conversione Σ <-> ς alla fine della parola. Nessuna <algorithm>soluzione di base può darti questo.


[1] Nel 2017, il Consiglio per l'ortografia tedesca ha stabilito che "ẞ" U + 1E9E LATTER CAPITAL LARTER SHARP S potrebbe essere utilizzato ufficialmente, come opzione accanto alla tradizionale conversione "SS" per evitare ambiguità, ad esempio nei passaporti (in cui i nomi sono scritti in maiuscolo ). Il mio bellissimo esempio, reso obsoleto dalla decisione della commissione ...


19
Questa è la risposta corretta nel caso generale. Lo standard non fornisce nulla per gestire qualsiasi cosa tranne "ASCII" tranne bugie e inganno. Ti fa pensare che forse potresti occuparti forse di UTF-16, ma non puoi. Come dice questa risposta, non è possibile ottenere la lunghezza caratteri appropriata (non la lunghezza byte) di una stringa UTF-16 senza eseguire la propria gestione Unicode. Se hai a che fare con testo reale, usa ICU. Grazie, @DevSolar
Espiazione limitata il

L'ICU è disponibile per impostazione predefinita su Ubuntu / Windows o deve essere installato separatamente? Inoltre che ne dite di questa risposta: stackoverflow.com/a/35075839/207661 ?
Shital Shah,

1
Ehi, guarda, una vera risposta! Grazie per avermi indicato nella direzione giusta, DevSolar.
Dan Bechard,

2
@DevSolar concordato! Il concetto di lunghezza è piuttosto insignificante sul testo (potremmo aggiungere legature all'elenco dei trasgressori). Detto questo, dal momento che le persone sono abituate a schede e controllare i caratteri che occupano un'unità di lunghezza, i punti di codice sarebbero la misura più intuitiva. Oh, e grazie per aver dato la risposta corretta, triste vederla così in basso :-(
masaers,

3
@LF Marginalmente meglio. Ma così tante cose non sono ancora coperte: touppere tolowerfunzionano ancora su singoli personaggi. La classe di stringa non ha ancora alcuna nozione di normalizzazione (ad esempio se un "ü" è codificato come "u con diaeresi" o "u + combinazione di diaeresi") o dove una stringa può o meno essere separata. L'elenco continua. u8string è (come le altre classi di stringhe standard) appropriato per "passare attraverso". Ma se vuoi elaborare Unicode, hai bisogno di ICU.
DevSolar,

36

Utilizzando range-based per loop di C ++ 11 un codice più semplice sarebbe:

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

9
Tuttavia, su una macchina francese, questo programma non converte i caratteri non ASCII consentiti in lingua francese. Ad esempio una stringa "Test String123. É Ï \ n 'verrà convertito in:' test string123. É Ï \ n 'sebbene i caratteri É Ï e le loro parti minuscole' é 'e' ï ', sono ammessi in francese. Sembra che nessuna soluzione per questo sia stata fornita da altri messaggi di questo thread.
incide il

Penso che sia necessario impostare un locale adeguato per questo.
user1095108

@incise, questo poi qualcuno ha pubblicato una risposta sull'ICU e questa è sicuramente la strada da percorrere. Più facile della maggior parte delle altre soluzioni che tenterebbero di comprendere le impostazioni locali.
Alexis Wilke,

Preferirei non usare librerie esterne quando possibile, personalmente.
kayleeFrye_onDeck l'


15

Questo è un seguito alla risposta di Stefan Mai: se si desidera posizionare il risultato della conversione in un'altra stringa, è necessario pre-allocare il suo spazio di archiviazione prima di chiamare std::transform. Poiché STL memorizza i caratteri trasformati nell'iteratore di destinazione (incrementandolo ad ogni iterazione del ciclo), la stringa di destinazione non verrà ridimensionata automaticamente e si rischia il calpestamento della memoria.

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

1
Questo non ha ridimensionato Ä in ä per me
Purefan il

Potrebbe anche utilizzare un iteratore inseritore posteriore qui invece del ridimensionamento manuale.
chili

11

Un altro approccio che utilizza range basato su loop con variabile di riferimento

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

cout<<test<<endl;

6

Per quanto vedo le librerie Boost sono davvero pessime dal punto di vista delle prestazioni. Ho testato il loro unordered_map su STL ed è stato in media 3 volte più lento (nel migliore dei casi 2, il peggio è stato di 10 volte). Anche questo algoritmo sembra troppo basso.

La differenza è così grande che sono sicuro che qualunque aggiunta tu debba fare tolowerper renderlo uguale a potenziare "per le tue esigenze" sarà molto più veloce di potenziare.

Ho eseguito questi test su un Amazon EC2, quindi le prestazioni sono state diverse durante il test, ma hai comunque avuto l'idea.

./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 reso così:

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

Fonte:

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

Immagino che dovrei fare i test su una macchina dedicata, ma userò questo EC2, quindi non ho davvero bisogno di testarlo sulla mia macchina.


1
Hai aperto le opzioni di ottimizzazione durante la compilazione? Penso che la libreria heavy boost STL dovrebbe funzionare meglio con un livello di ottimizzazione elevato.
Wei Song,

1
Ho usato -O2 in uno dei test e nient'altro.
Etherealone,

2
Le prestazioni di unordered_map dipendono dall'algoritmo di hashing combinato con i dati che stai utilizzando. Non esiste un algoritmo di hashing magico che funzioni per tutti e tutti i dati per rendere unordered_map il più velocemente possibile. Benchmark e provare cose diverse. Il motivo per cui stai peggiorando le prestazioni, è perché con l'hash che stai utilizzando stai ottenendo molte collisioni, che in sostanza provoca una ricerca in un elenco. Visita questo sito per maggiori informazioni: fgda.pl/post/7/gcc-hash-map-vs-unordered-map Per i miei scopi, la funzione fornita al link ha ridotto le collisioni e quindi è stata molto veloce.
leetNightshade,

6

Il modo più semplice per convertire la stringa in minuscolo senza preoccuparsi dello spazio dei nomi standard è il seguente

1: stringa con / senza spazi

#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: stringa senza spazi

#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

std::ctype::tolower()dalla libreria di localizzazione C ++ standard lo farà correttamente per te. Ecco un esempio estratto dalla pagina di riferimento 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";
}

Bello, purché tu possa convertire i personaggi sul posto. Cosa succede se la stringa di origine è const? Ciò sembra renderlo un po 'più disordinato (ad esempio, non sembra che tu possa usare f.tolower()), poiché devi inserire i caratteri in una nuova stringa. Useresti transform()e qualcosa del genere std::bind1st( std::mem_fun() )per l'operatore?
Quazar,

Per una stringa const, possiamo semplicemente creare una copia locale e poi convertirla in posizione.
Sameer,

Sì, però, fare una copia aggiunge più sovraccarico.
Quazar,

È possibile utilizzare std :: transform con la versione di ctype :: tolower che non accetta i puntatori. Usa un adattatore per iteratore con inserto posteriore e non dovrai nemmeno preoccuparti di ridimensionare la stringa di output.
chili

Fantastico, soprattutto perché in libstdc ++ tolowercon localeparametro, la chiamata implicita a use_facetsembra essere un collo di bottiglia nelle prestazioni. Uno dei miei colleghi ha ottenuto un aumento di velocità del 100% sostituendo boost::iequals(che presenta questo problema) con una versione che use_facetviene chiamata solo una volta al di fuori del ciclo.
Arne Vogel,

3

Un'alternativa a Boost è POCO (pocoproject.org).

POCO offre due varianti:

  1. La prima variante crea una copia senza alterare la stringa originale.
  2. La seconda variante modifica la stringa originale in posizione.
    Le versioni "In Place" hanno sempre "InPlace" nel nome.

Entrambe le versioni sono illustrate di seguito:

#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

C'è un modo per convertire maiuscole in minuscole SENZA eseguire test ed è piuttosto semplice. L'uso della funzione / macro di clocale.h da parte della funzione isupper () dovrebbe occuparsi dei problemi relativi alla tua posizione, ma in caso contrario, puoi sempre modificare l'UtoL [] in base al contenuto del tuo cuore.

Dato che i caratteri di C sono in realtà solo in 8 bit (ignorando per ora i set di caratteri di grandi dimensioni) è possibile creare un array di 256 byte contenente un set di caratteri alternativo e nella funzione di conversione utilizzare i caratteri nella stringa come pedice nel array di conversione.

Invece di un mapping 1-per-1, tuttavia, assegnare ai membri dell'array maiuscoli i valori int BYTE per i caratteri minuscoli. Puoi trovare islower () e isupper () utili qui.

inserisci qui la descrizione dell'immagine

Il codice è simile al seguente ...

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

Questo approccio, allo stesso tempo, ti permetterà di rimappare qualsiasi altro personaggio che desideri cambiare.

Questo approccio ha un enorme vantaggio quando si esegue su processori moderni, non è necessario eseguire la previsione di diramazione in quanto non esistono test per la ramificazione. Ciò consente di risparmiare la logica di predizione della diramazione della CPU per altri loop e tende a prevenire blocchi della pipeline.

Alcuni qui potrebbero riconoscere questo approccio come lo stesso usato per convertire EBCDIC in ASCII.


2
"Esiste un modo per convertire maiuscole in minuscole SENZA eseguire test" mai sentito parlare di tabelle di ricerca?
Gábor Buella,

1
Comportamento indefinito per caratteri negativi.
Roland Illig,

Le CPU moderne hanno un collo di bottiglia nella memoria e non nella CPU. Il benchmarking sarebbe interessante.
Contango

3

Dal momento che nessuna delle risposte menzionava la prossima libreria Ranges, che è disponibile nella libreria standard dal C ++ 20 e attualmente disponibile separatamente su GitHub come range-v3, vorrei aggiungere un modo per eseguire questa conversione utilizzandola.

Per modificare la stringa sul posto:

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

Per generare una nuova stringa:

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

(Non dimenticare di #include <cctype>e le intestazioni Ranges richieste.)

Nota: l'uso di unsigned charcome argomento per la lambda è ispirato da cppreference , che afferma:

Come tutte le altre funzioni di <cctype>, il comportamento di std::tolowernon è definito se il valore dell'argomento non è né rappresentabile unsigned charné uguale EOF. Per usare queste funzioni in modo sicuro con semplici chars (o signed chars), l'argomento dovrebbe prima essere convertito in unsigned char:

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

Allo stesso modo, non dovrebbero essere usati direttamente con algoritmi standard quando il tipo di valore dell'iteratore è charo signed char. Invece, converti il ​​valore in unsigned charprimo:

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

3

Le mie funzioni di template che eseguono maiuscole / minuscole.

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

Questo è quello di cui avevo bisogno. Ho appena usato il towlowerper caratteri larghi che supporta l'UTF-16.
Juv,

2

Ecco una tecnica macro se vuoi qualcosa di semplice:

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

Tuttavia, nota che il commento di @ AndreasSpindler su questa risposta è ancora una considerazione importante, tuttavia, se stai lavorando su qualcosa che non è solo caratteri ASCII.


1
Lo sto ridimensionando per dare macro quando esiste una soluzione perfettamente valida: tu dai anche quelle soluzioni.
Più chiaro il

2
La tecnica macro significa meno tipizzazione del codice per qualcosa che comunemente si userebbe molto in programmazione. Perché non usarlo? Altrimenti, perché avere delle macro?
Volomike,

3
Le macro sono un retaggio di C su cui si sta lavorando sodo per sbarazzarsi di. Se si desidera ridurre la quantità di battitura, utilizzare una funzione o una lambda. void strtoupper(std::string& x) { std::transform (x.begin(), x.end(), x.begin(), ::toupper); }
Più chiaro il

1
@Clearer Dato che voglio essere un programmatore migliore, puoi fornirmi qualsiasi collegamento doc ANSI in cui qualsiasi comitato ANSI C ++ dica qualcosa a effetto di "Dobbiamo convocare una riunione per sbarazzarci di macro dal C ++"? O qualche altra tabella di marcia?
Volomike,

2
No, non posso. Tuttavia, la posizione di Bjarne sull'argomento è stata chiarita in diverse occasioni. Inoltre, ci sono molte ragioni per non usare le macro in C e C ++. xpotrebbe essere un'espressione valida, che si compila correttamente ma darà risultati completamente falsi a causa delle macro.
Più chiaro l'

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

Per maggiori informazioni: http://www.cplusplus.com/reference/locale/tolower/


2

Esiste un'alternativa che funziona il 100% delle volte?

No

Esistono diverse domande che è necessario porsi prima di scegliere un metodo di lettere minuscole.

  1. Come viene codificata la stringa? semplice ASCII? UTF-8? qualche forma di codifica legacy ASCII estesa?
  2. Cosa intendi con minuscolo comunque? Le regole di mappatura dei casi variano tra le lingue! Vuoi qualcosa che sia localizzato nelle impostazioni locali degli utenti? vuoi qualcosa che si comporti in modo coerente su tutti i sistemi su cui gira il tuo software? Vuoi solo minuscoli caratteri ASCII e passare attraverso tutto il resto?
  3. Quali librerie sono disponibili?

Una volta che hai le risposte a queste domande, puoi iniziare a cercare una soluzione adatta alle tue esigenze. Non esiste una taglia unica che vada bene per tutti ovunque!


2

Prova questa funzione :)

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

1

Su piattaforme Microsoft puoi utilizzare la strlwrfamiglia di funzioni: 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

Snippet di codice

#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

Copia perché non è stato consentito per migliorare la risposta. Grazie mille


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

Spiegazione:

for(auto& c : test)è un range-based per loop del tipo :
for (range_declaration:range_expression)loop_statement

  1. range_declaration: auto& c
    Qui l' identificatore automatico viene utilizzato per la detrazione automatica del tipo. Quindi il tipo viene dedotto dall'inizializzatore delle variabili.

  2. range_expression: test
    L'intervallo in questo caso sono i caratteri della stringa test.

I caratteri della stringa testsono disponibili come riferimento all'interno dell'identificatore del ciclo for c.


Per favore, chiarisci da dove hai copiato la tua risposta.
bfontaine,

0

C ++ non ha implementato metodi tolower o toupper per stringa, ma è disponibile per char. Si può facilmente leggere ogni carattere di stringa, convertirlo nel caso richiesto e rimetterlo in stringa. Un codice di esempio senza utilizzare alcuna libreria di terze parti:

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

Per operazioni basate sui caratteri su stringa: per ogni carattere nella stringa


-1

Questa potrebbe essere un'altra versione semplice per convertire maiuscole in minuscole e viceversa. Ho usato la versione della community VS2017 per compilare questo codice sorgente.

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

Nota: se sono presenti caratteri speciali, è necessario gestirli mediante il controllo delle condizioni.


-8

Ho provato std :: transform, tutto quello che ottengo è un abominevole errore di compilazione criptic stl che solo i druidi di 200 anni fa possono capire (non riesco a convertire da influenza di flabidi ai flibidi)

funziona benissimo e può essere facilmente modificato

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;
}
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.