Domanda

Voglio convertire a std::string in minuscolo.Sono a conoscenza della funzione tolower(), tuttavia in passato ho avuto problemi con questa funzione e comunque non è l'ideale come utilizzo con a std::string richiederebbe l'iterazione su ciascun carattere.

Esiste un'alternativa che funzioni al 100% delle volte?

È stato utile?

Soluzione

Adattato da Non Domande 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); });

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

Se davvero odi tolower() , ecco uno specialista Alternativa solo 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);

Tieni presente che <=> può fare solo una sostituzione per singolo byte di carattere, che non è adatta per molti script, specialmente se si utilizza una codifica multi-byte come UTF-8.

Altri suggerimenti

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 i non interni posto :

#include <boost/algorithm/string.hpp>

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

tl; dr

Utilizza la libreria ICU .


Per prima cosa devi rispondere a una domanda: qual è la codifica del tuo std::string? È ISO-8859-1? O forse ISO-8859-8? O Windows Codepage 1252? Qualunque cosa tu stia utilizzando per convertire maiuscole e minuscole, lo sa? (O fallisce miseramente per i personaggi oltre 0x7f?)

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

E non appena provi qualcosa come "SS", nella qualsiasi codifica, sei nei guai seri. (Perché semplicemente non è possibile eseguire questo & Quot; giusto & Quot; con la libreria standard, che può fornire solo un carattere risultante, non il std::tolower( 'I' ) necessario qui.) [ 1] Un altro esempio potrebbe essere 'i', che dovrebbe produrre risultati diversi a seconda della lingua . In Germania, 'ı' sarebbe corretto; in Turchia, std::u16string (LATIN SMALL LOTTER DOTLESS I) è il risultato atteso (che, di nuovo, è più di un byte nella codifica UTF-8).

Quindi il punto è che la libreria standard dipende da quali locali sono supportate sulla macchina su cui è in esecuzione il software ... e cosa fai se non lo è?

Quindi quello che stai veramente è una classe di stringhe che è in grado di gestire correttamente tutto ciò, e che è non std::u32string .

(Nota C ++ 11: <=> e <=> sono migliori , ma ancora non perfetti.)

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

E credimi, ottenere Potenziare la compilazione con ICU a volte può essere un vero dolore. (Non ci sono file binari precompilati per Windows, quindi dovresti fornirli insieme alla tua applicazione e che apre una nuova lattina di worm ...)

Quindi personalmente consiglierei di ottenere il pieno supporto Unicode direttamente dalla bocca del cavallo e di utilizzare direttamente la libreria ICU :

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

Compila (con G ++ in questo esempio):

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

Questo dà:

eidengesäß
EIDENGESÄSS

[1] Nel 2017, il Consiglio per l'ortografia tedesca ha stabilito che " & # 7838; " U + 1E9E LETTERA MAIALE LATINA SHARP S potrebbe essere utilizzata ufficialmente, come opzione accanto al tradizionale & Quot; SS & Quot; conversione per evitare ambiguità, ad es. nei passaporti (in cui i nomi sono in maiuscolo). Il mio bellissimo esempio, reso obsoleto dalla decisione della commissione ...

Se la stringa contiene caratteri UTF-8 al di fuori dell'intervallo ASCII, allora boost :: algoritmo :: to_lower non li convertirà. Meglio usare boost :: locale :: to_lower quando è coinvolto UTF-8. Vedi http://www.boost.org/doc/libs/1_51_0 /libs/locale/doc/html/conversions.html

Utilizzando range-based per il ciclo 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);
}

Questo è un seguito alla risposta di Stefan Mai: se desideri posizionare il risultato della conversione in un'altra stringa, devi 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;
}

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;

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 per tolower per renderla uguale per aumentare " per le tue esigenze " sarà molto più veloce di boost.

Ho eseguito questi test su un Amazon EC2, quindi le prestazioni sono cambiate 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 lo ha 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();

Suppongo che dovrei fare i test su una macchina dedicata, ma userò questo EC2, quindi non ho davvero bisogno di testarlo sul mio computer.

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

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

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.
    " In Place " le versioni 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);

Esiste un modo per convertire le maiuscole in SENZA eseguire i 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 pedici nella matrice di conversione.

Invece di una mappatura 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 ti permetterà, allo stesso tempo, 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.

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, tieni presente che il commento di @ AndreasSpindler su questa risposta è ancora una considerazione importante, tuttavia, se stai lavorando su qualcosa che non è solo caratteri ASCII.

// 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 ulteriori informazioni: http://www.cplusplus.com/reference/locale/ tolower /

  

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!

Poiché nessuna delle risposte menzionava la prossima libreria Ranges, disponibile nella libreria standard a partire da C++20 e attualmente disponibile separatamente su GitHub COME range-v3, vorrei aggiungere un modo per eseguire questa conversione utilizzandolo.

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 dimenticartelo #include <cctype> e le intestazioni Intervalli richieste.)

Nota:l'impiego di unsigned char a cui si ispira l'argomento della lambda cppreference, quali Stati:

Come tutte le altre funzioni di <cctype>, il comportamento di std::tolower non è definito se il valore dell'argomento non è rappresentabile come unsigned char né uguale a EOF.Per utilizzare queste funzioni in modo sicuro con plain chars (o signed chars), l'argomento deve essere prima 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 utilizzati direttamente con algoritmi standard quando il tipo di valore dell'iteratore lo è char O signed char.Converti invece il valore in unsigned char Primo:

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

Su piattaforme Microsoft puoi utilizzare la strlwr famiglia 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 );
}

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

Usa fplus :: to_lower_case ().

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

Cerca 'to_lower_case' in http://www.editgym.com/fplus-api -search / )

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

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


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

Spiegazione:

for(auto& c : test) è un basato su intervallo per loop di il tipo
for (range_declaration:range_expression)loop_statement :

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

  2. c : <=>
    L'intervallo in questo caso sono i caratteri della stringa <=>.

I caratteri della stringa <=> sono disponibili come riferimento all'interno dell'identificatore del ciclo for <=>.

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

Le mie funzioni modello 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);
}

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 utilizzando il controllo delle condizioni.

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 (impossibile convertire in influenza da flabidi di 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;
}
//You can really just write one on the fly whenever you need one.
#include <string>
void _lower_case(std::string& s){
for(unsigned short l = s.size();l;s[--l]|=(1<<5));
}
//Here is an example.
//http://ideone.com/mw2eDK
Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top