wake-up-neo.com

Wie konvertiert man std :: string in Kleinbuchstaben?

Ich möchte einen std::string in Kleinbuchstaben konvertieren. Ich kenne die Funktion tolower(), aber in der Vergangenheit hatte ich Probleme mit dieser Funktion, und es ist ohnehin kaum ideal, da die Verwendung mit einem std::string das Iterieren jedes Zeichens erfordert.

Gibt es eine Alternative, die zu 100% funktioniert?

662
Konrad

Von this :

#include <algorithm>
#include <string> 

std::string data = "Abc"; 
std::transform(data.begin(), data.end(), data.begin(), ::tolower);

Sie werden wirklich nicht durchkommen, indem Sie jeden Charakter durchlaufen. Es gibt keine Möglichkeit zu wissen, ob das Zeichen ansonsten Klein- oder Großbuchstaben ist.

Wenn Sie tolower() wirklich hassen, haben Sie hier eine nicht tragbare Alternative, die ich Ihnen nicht empfehlen sollte:

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

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

Beachten Sie, dass ::tolower() nur eine Ersetzung pro Einzelbyte-Zeichen durchführen kann, was für viele Skripte ungeeignet ist, insbesondere wenn Sie eine Multibyte-Codierung wie UTF-8 verwenden.

803
Stefan Mai

Dafür gibt es einen Boost-String-Algorithmus:

#include <boost/algorithm/string.hpp>    

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

Oder für nicht in-place:

#include <boost/algorithm/string.hpp>    

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

tl; dr

Verwenden Sie die Bibliothek ICU .


Zuerst müssen Sie eine Frage beantworten: Was ist das encoding Ihres std::string? Ist es ISO-8859-1? Oder vielleicht ISO-8859-8? Oder Windows-Codepage 1252? Weiß das, was Sie zum Konvertieren von Groß- zu Kleinbuchstaben verwenden, das? (Oder schlägt es für Zeichen über 0x7f kläglich fehl?)

Wenn Sie UTF-8 (die einzige vernünftige Wahl unter den 8-Bit-Kodierungen) mit std::string als Container verwenden, täuschen Sie sich bereits im Glauben, dass Sie immer noch die Kontrolle über die Dinge haben, weil Sie eine Multibyte-Zeichenfolge in einem Container, der das Multibyte-Konzept nicht kennt. Sogar etwas so einfaches wie .substr() ist eine tickende Zeitbombe. (Weil das Aufteilen einer Multibyte-Sequenz zu einer ungültigen (Unter-) Zeichenfolge führt.)

Und sobald Sie etwas wie std::toupper( 'ß' ) in any encoding versuchen, haben Sie große Probleme. (Weil es einfach nicht möglich ist, dies mit der Standardbibliothek "richtig" zu tun, die nur das Ergebniszeichen one liefern kann, nicht den hier benötigten "SS".) [1] Ein anderes Beispiel wäre std::tolower( 'I' ), das nachgeben sollte unterschiedliche Ergebnisse abhängig vom Gebietsschema. In Deutschland wäre 'i' richtig; In der Türkei ist 'ı' (lateinischer kleiner Buchstabe DOTLESS I) das erwartete Ergebnis.

Dann gibt es den Punkt, dass die Standardbibliothek davon abhängt, welche Gebietsschemas unterstützt auf dem Computer sind, auf dem Ihre Software läuft ... und was tun Sie, wenn dies nicht der Fall ist?

Was Sie also wirklich suchen, ist eine String-Klasse, die in der Lage ist, mit all dem richtig umzugehen, und das ist nichtstd::string.

(C++ 11-Hinweis: std::u16string und std::u32string sind besser, aber immer noch nicht perfekt.)

Während Boost aussieht Schön ist, ist API Boost.Locale im Grunde ein Wrapper um ICU . Wenn Boost ist kompiliert mit Unterstützung von ICU ... Wenn dies nicht der Fall ist, ist Boost.Locale auf die Unterstützung für das Gebietsschema beschränkt, die für die Standardbibliothek kompiliert wurde.

Und glauben Sie mir, Getting Boost zum Kompilieren mit ICU kann manchmal sehr schmerzhaft sein. (Es gibt keine vorkompilierten Binärdateien für Windows. Sie müssen sie also zusammen mit Ihrer Anwendung angeben und that öffnet eine völlig neue Dose Würmer ...)

Daher würde ich persönlich empfehlen, eine vollständige Unicode-Unterstützung direkt aus dem Munde des Pferdes zu erhalten und die Bibliothek ICU direkt zu verwenden:

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

Kompilieren (in diesem Beispiel mit G ++):

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

Das gibt:

eidengesäß
EIDENGESÄSS

[1] 2017 beschloss der Rat für deutsche Orthographie, dass "ẞ" U + 1E9E LATIN CAPITAL LETTER SHARP S offiziell als Option verwendet werden kann, als Option neben der traditionellen "SS" -Konvertierung, um Mehrdeutigkeiten zu vermeiden, z. in Pässen (bei denen Namen groß geschrieben werden). Mein schönes Go-To-Beispiel, das durch die Entscheidung des Ausschusses obsolet wurde ... 

193
DevSolar

Wenn der String UTF-8-Zeichen außerhalb des Bereichs ASCII enthält, werden diese von boost :: algorithm :: to_lower nicht konvertiert. Verwenden Sie boost :: locale :: to_lower besser, wenn UTF-8 beteiligt ist. Siehe http://www.boost.org/doc/libs/1_51_0/libs/locale/doc/html/conversions.html

28
Patrick Ohly

Die Verwendung einer bereichsbasierten for-Schleife für C++ 11 wäre ein einfacherer Code:

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

Dies ist eine Fortsetzung der Antwort von Stefan Mai: Wenn Sie das Ergebnis der Konvertierung in einen anderen String einfügen möchten, müssen Sie seinen Speicherplatz vor dem Aufruf von std::transform vorbelegen. Da STL transformierte Zeichen im Ziel-Iterator speichert (bei jeder Iteration der Schleife inkrementiert), wird die Größe des Ziel-Strings nicht automatisch geändert, und es besteht die Gefahr, dass Speicherplatz zerstört wird.

#include <string>
#include <algorithm>
#include <iostream>

int main (int argc, char* argv[])
{
  std::string sourceString = "Abc";
  std::string destinationString;

  // Allocate the destination space
  destinationString.resize(sourceString.size());

  // Convert the source string to lower case
  // storing the result in destination string
  std::transform(sourceString.begin(),
                 sourceString.end(),
                 destinationString.begin(),
                 ::tolower);

  // Output the result of the conversion
  std::cout << sourceString
            << " -> "
            << destinationString
            << std::endl;
}
14
user2218467

Ein weiterer Ansatz, bei dem der Bereich für die Schleife mit der Referenzvariablen verwendet wird

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

cout<<test<<endl;
8
Gilson PJ

Soweit ich sehe, sind Boost-Bibliotheken wirklich leistungsmäßig schlecht. Ich habe ihre unordered_map auf STL getestet und es war durchschnittlich 3 Mal langsamer (bester Fall 2, am schlechtesten war 10 Mal). Auch dieser Algorithmus erscheint zu niedrig.

Der Unterschied ist so groß, dass ich sicher bin, dass alle Änderungen, die Sie an tolower vornehmen müssen, um "für Ihre Bedürfnisse" zu erhöhen, viel schneller als Boost sind.

Ich habe diese Tests auf einem Amazon EC2 durchgeführt, daher schwankte die Leistung während des Tests, aber Sie haben immer noch die Idee.

./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 hat es so gemacht:

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

Quelle:

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

Ich denke, ich sollte die Tests auf einer dedizierten Maschine durchführen, aber ich werde diese EC2 verwenden, sodass ich sie nicht wirklich auf meiner Maschine testen muss.

7
Etherealone

Der einfachste Weg, um string in loweercase zu konvertieren, ohne sich um std-Namespace zu kümmern, ist wie folgt 

1: Zeichenfolge mit/ohne Leerzeichen

#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: Zeichenfolge ohne Leerzeichen

#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
Atul Rokade

std::ctype::tolower() aus der Standard-C++ - Lokalisierungsbibliothek erledigt dies korrekt für Sie. Hier ist ein Beispiel aus der Tolower-Referenzseite

#include <locale>
#include <iostream>

int main () {
  std::locale::global(std::locale("en_US.utf8"));
  std::wcout.imbue(std::locale());
  std::wcout << "In US English UTF-8 locale:\n";
  auto& f = std::use_facet<std::ctype<wchar_t>>(std::locale());
  std::wstring str = L"HELLo, wORLD!";
  std::wcout << "Lowercase form of the string '" << str << "' is ";
  f.tolower(&str[0], &str[0] + str.size());
  std::wcout << "'" << str << "'\n";
}
5
Sameer

Eine Alternative zu Boost ist POCO (pocoproject.org).

POCO bietet zwei Varianten an:

  1. Die erste Variante erstellt eine Kopie, ohne die ursprüngliche Zeichenfolge zu ändern.
  2. Die zweite Variante ändert die ursprüngliche Zeichenfolge.
    "In Place" -Versionen haben immer "InPlace" im Namen. 

Beide Versionen werden nachfolgend gezeigt:

#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
Jason Enochs

Es gibt eine Möglichkeit, Großbuchstaben in untere OHNE bei Tests umzuwandeln, und das ist ziemlich einfach. Die Verwendung von clocale.h durch die isupper () - Funktion/dieses Makros sollte sich mit Problemen in Bezug auf Ihren Standort befassen. Wenn nicht, können Sie die UtoL [] immer an den Inhalt Ihres Herzens anpassen. 

Da die Zeichen von C eigentlich nur 8-Bit-Ints sind (wobei die breiten Zeichensätze für den Moment ignoriert werden), können Sie ein 256-Byte-Array erstellen, das einen alternativen Zeichensatz enthält, und in der Konvertierungsfunktion die Zeichen in Ihrer Zeichenfolge als Subskription in die Zeichenfolge verwenden Konvertierungsarray. 

Geben Sie den Mitgliedern des Arrays mit Großbuchstaben statt der 1-für-1-Zuordnung die BYTE int-Werte für die Kleinbuchstaben an. Hier finden Sie islower () und isupper ()

enter image description here

Der Code sieht so aus ...

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

Auf diese Weise können Sie gleichzeitig andere Zeichen, die Sie ändern möchten, neu zuordnen. 

Dieser Ansatz hat einen großen Vorteil, wenn er auf modernen Prozessoren ausgeführt wird. Es ist nicht erforderlich, eine Verzweigungsvorhersage zu treffen, da keine Tests vorhanden sind, die Verzweigungen umfassen. Dies spart die Verzweigungsvorhersagelogik der CPU für andere Schleifen und verhindert das Stoppen der Pipeline. 

Einige hier können diesen Ansatz als den gleichen Ansatz erkennen, mit dem EBCDIC in ASCII konvertiert wird.

2
user2548100

Hier ist eine Makrotechnik, wenn Sie etwas einfaches wollen:

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

Beachten Sie jedoch, dass @ AndreasSpindlers Kommentar zu diese Antwort immer noch eine wichtige Überlegung ist, wenn Sie an etwas arbeiten, das nicht nur ASCII - Zeichen ist.

2
Volomike

Gibt es eine Alternative, die zu 100% funktioniert?

Nein

Sie müssen sich einige Fragen stellen, bevor Sie eine Absenkungsmethode wählen.

  1. Wie ist der String kodiert? einfaches ASCII? UTF-8? eine Form der erweiterten ASCII - Altcodierung?
  2. Was meinst du mit Kleinschreibung? Case-Mapping-Regeln variieren zwischen den Sprachen! Möchten Sie etwas, das auf das Gebietsschema des Benutzers lokalisiert ist? Möchten Sie etwas, das sich auf allen Systemen, auf denen Ihre Software läuft, konsistent verhält? Möchten Sie nur ASCII Zeichen in Kleinbuchstaben schreiben und alles durchgehen? 
  3. Welche Bibliotheken gibt es? 

Sobald Sie Antworten auf diese Fragen haben, können Sie nach einer Lösung suchen, die Ihren Bedürfnissen entspricht. Es gibt keine Einheitsgröße für alle, die für jeden funktioniert!

1
plugwash

Da in keiner der Antworten die bevorstehende Ranges-Bibliothek erwähnt wurde, die in der Standardbibliothek seit C++ 20 verfügbar ist und derzeit separat auf GitHub als range-v3 verfügbar ist, möchte ich eine Möglichkeit hinzufügen, diese Konvertierung mithilfe dieser Bibliothek durchzuführen .

So ändern Sie die Zeichenfolge an Ort und Stelle:

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

So generieren Sie eine neue Zeichenfolge:

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

(Vergessen Sie nicht, #include <cctype> und die erforderlichen Ranges-Header anzugeben.)

Hinweis: Die Verwendung von unsigned char als Argument für das Lambda ist von cppreference inspiriert, das Folgendes angibt:

Wie bei allen anderen Funktionen von <cctype> ist das Verhalten von std::tolower nicht definiert, wenn der Wert des Arguments weder als unsigned char darstellbar noch gleich EOFist. Um diese Funktionen sicher mit einfachen charname__s (oder signed chars) zu verwenden, sollte das Argument zuerst in unsigned char konvertiert werden:

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

Ebenso sollten sie nicht direkt mit Standardalgorithmen verwendet werden, wenn der Wertetyp des Iterators charoder signed char ist. Konvertieren Sie stattdessen zuerst den Wert in unsigned 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;
}
1
L. F.
// 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;
}

Weitere Informationen: http://www.cplusplus.com/reference/locale/tolower/

1
MoraRockey

Auf Microsoft-Plattformen können Sie die strlwr-Funktionsfamilie verwenden: 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
Autodidact

In C++ sind keine Tolower- oder Toupper-Methoden für Zeichenfolgen implementiert, sie sind jedoch für Zeichen verfügbar. Man kann leicht jedes Zeichen einer Zeichenkette lesen, es in die erforderliche Schreibweise umwandeln und es wieder in eine Zeichenkette einfügen. Ein Beispielcode ohne Verwendung einer Drittanbieter-Bibliothek:

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

Für zeichenbasierte Operationen mit Zeichenfolgen: Für jedes Zeichen in Zeichenfolgen

0
Mahipal

Meine eigenen Vorlagenfunktionen, die Groß-/Kleinschreibung ausführen.

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

Code-Auszug

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

Verwenden Sie fplus :: to_lower_case ().

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

Suchen Sie nach "to_lower_case" in http://www.editgym.com/fplus-api-search/ )

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

Dies könnte eine andere einfache Version sein, um Großbuchstaben in Kleinbuchstaben zu konvertieren und umgekehrt. Ich habe die Community-Version VS2017 verwendet, um diesen Quellcode zu kompilieren.

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

Hinweis: Wenn Sonderzeichen vorhanden sind, müssen sie mit der Bedingungsprüfung behandelt werden.

0
Praveer Kumar

Kopieren, weil es zur Verbesserung der Antwort nicht zulässig ist. Danke SO


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

Erklärung:

for(auto& c : test) ist eine bereichsbasierte for-Schleife der Art 
for (range_declaration:range_expression)loop_statement:

  1. range_declaration: auto& c
    Hier wird der automatische Bezeichner für die automatische Typabzugung verwendet. Der Typ wird also vom Variableninitialisierer abgezogen.

  2. range_expression: test
    Der Bereich sind in diesem Fall die Zeichen der Zeichenfolge test

Die Zeichen der Zeichenfolge test stehen als Referenz in der for-Schleife durch den Bezeichner c zur Verfügung.

0
goulashsoup