Wie konvertiere ich std :: string in Kleinbuchstaben?


777

Ich möchte a std::stringin Kleinbuchstaben umwandeln . Ich bin mir der Funktion bewusst tolower(), aber in der Vergangenheit hatte ich Probleme mit dieser Funktion und sie ist sowieso kaum ideal, da die Verwendung mit a std::stringdas Durchlaufen jedes Zeichens erfordern würde.

Gibt es eine Alternative, die 100% der Zeit funktioniert?


34
Wie sonst würden Sie jedes Element einer Liste von etwas in etwas anderes konvertieren, ohne die Liste zu durchlaufen? Eine Zeichenfolge ist nur eine Liste von Zeichen. Wenn Sie auf jedes Zeichen eine Funktion anwenden müssen, müssen Sie die Zeichenfolge durchlaufen. Daran führt kein Weg vorbei.

14
Warum genau senkt diese Frage die Bewertung? Ich habe kein Problem damit, meine Zeichenfolge zu durchlaufen, aber ich frage, ob es außer tolower (), toupper () usw. noch andere Funktionen gibt
Konrad

3
Wenn Sie ein Zeichenarray im C-Stil haben, können Sie möglicherweise jedem Block mit 4 Zeichen (sofern ALLE bereits Großbuchstaben sind) ox20202020 hinzufügen, um jeweils 4 Zeichen in Kleinbuchstaben umzuwandeln.

13
@Dan: Wenn sie möglicherweise bereits in Kleinbuchstaben geschrieben sind, aber definitiv AZ oder az sind, können Sie ODER mit 0x20 ODER anstatt hinzufügen. Eine dieser so klugen, wahrscheinlich dummen Optimierungen, die sich fast nie lohnen ...
Steve Jessop

4
Ich weiß nicht, warum es herabgestimmt worden wäre ... sicherlich ist es ein wenig seltsam formuliert (weil Sie jeden Punkt irgendwie durchlaufen müssen), aber es ist eine gültige Frage
Warren

Antworten:


905

Angepasst an nicht so häufig gestellte Fragen :

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

Du wirst wirklich nicht davonkommen, ohne jeden Charakter zu durchlaufen. Es gibt keine Möglichkeit festzustellen, ob das Zeichen sonst in Klein- oder Großbuchstaben geschrieben ist.

Wenn Sie es wirklich hassen tolower(), finden Sie hier eine spezielle ASCII-Alternative, die Sie nicht empfehlen:

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

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

Beachten Sie, dass tolower()nur eine Ersetzung pro Einzelbytezeichen möglich ist, was für viele Skripte nicht geeignet ist, insbesondere wenn eine Mehrbyte-Codierung wie UTF-8 verwendet wird.


25
(Alt mag es sein, die fraglichen Algorithmen haben sich kaum verändert) @Stefan Mai: Welche Art von "ganzem Overhead" gibt es beim Aufrufen von STL-Algorithmen? Die Funktionen sind eher schlank (dh einfach für Schleifen) und oft inline, da Sie selten viele Aufrufe derselben Funktion mit denselben Vorlagenparametern in derselben Kompilierungseinheit haben.
Äq.

257
Jedes Mal, wenn Sie annehmen, dass Charaktere ASCII sind, tötet Gott ein Kätzchen. :(
Brian Gordon

13
Ihr erstes Beispiel weist möglicherweise ein undefiniertes Verhalten auf (Übergabe charan ::tolower(int)). Sie müssen sicherstellen, dass Sie keinen negativen Wert übergeben.
Juanchopanza

37
-1 Diese Verwendung von ::tolowerkann durchaus abstürzen, es ist UB für Nicht-ASCII-Eingabe.
Prost und hth. - Alf

7
Das :: wird vor tolower benötigt, um anzuzeigen, dass es sich im äußersten Namespace befindet. Wenn Sie diesen Code in einem anderen Namespace verwenden, gibt es möglicherweise eine andere (möglicherweise nicht verwandte) Definition von tolower, die ohne das :: bevorzugt ausgewählt wird.
Charles Ofria

320

Boost bietet hierfür einen 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);

2
Ich gehe davon aus, dass dies nicht die gleichen Probleme hat wie bei ASCII-Eingaben.
Paulm

19
Schlägt für Nicht-ASCII-7 fehl.
DevSolar

1
Gibt es eine nicht vorhandene Version davon?
Ray

5
@ Ray, ja,to_lower_copy
smac89

234

tl; dr

Verwenden Sie die ICU-Bibliothek . Wenn Sie dies nicht tun, wird Ihre Konvertierungsroutine in Fällen, in denen Sie wahrscheinlich gar nicht wissen, dass sie existieren, stillschweigend unterbrochen.


Zuerst müssen Sie eine Frage beantworten: Wie lautet die Kodierung Ihrer std::string? Ist es ISO-8859-1? Oder vielleicht ISO-8859-8? Oder Windows Codepage 1252? Weiß das, was auch immer Sie zum Konvertieren von Groß- und Kleinbuchstaben verwenden? (Oder scheitert es kläglich für Charaktere vorbei 0x7f?)

Wenn Sie UTF-8 (die einzig vernünftige Wahl unter den 8-Bit-Codierungen) mit std::stringals Container verwenden, täuschen Sie sich bereits in der Annahme, dass Sie immer noch die Kontrolle über die Dinge haben, da Sie eine Multibyte-Zeichenfolge in einem Container speichern das ist sich des Multibyte-Konzepts nicht bewusst. Sogar etwas so Einfaches wie .substr()eine tickende Zeitbombe. (Da das Aufteilen einer Multibyte-Sequenz zu einer ungültigen (Unter-) Zeichenfolge führt.)

Und sobald Sie etwas versuchen std::toupper( 'ß' ), in irgendeiner Codierung, sind Sie in großen Schwierigkeiten. (Weil es mit der Standardbibliothek, die nur ein Ergebniszeichen liefern kann , das "SS"hier nicht benötigt wird , einfach nicht "richtig" ist.) [1] Ein anderes Beispiel wäre std::tolower( 'I' ), das je nach Gebietsschema unterschiedliche Ergebnisse liefern sollte . In Deutschland 'i'wäre das richtig; In der Türkei ist 'ı'(LATIN SMALL LETTER DOTLESS I) das erwartete Ergebnis (das wiederum mehr als ein Byte in der UTF-8-Codierung beträgt). Ein weiteres Beispiel ist das griechische Sigma , Groß- '∑'und Kleinschreibung 'σ'... außer am Ende eines Wortes, wo es sich befindet 'ς'.

Also, jeder Fall Konvertierung , die auf einem Zeichen in einer Zeit arbeitet, oder noch schlimmer, ein Byte zu einem Zeitpunkt, wird durch Design gebrochen.

Dann gibt es den Punkt , dass die Standard - Bibliothek, für das, was es ist dazu in der Lage ist abhängig davon , welche Lokalisationen werden unterstützt auf der Maschine Ihre Software auf läuft ... und was tun Sie , wenn es nicht ist?

Also , was Sie wirklich suchen, ist ein String - Klasse , die mit all dies zu tun richtig fähig ist, und das ist nicht eine der std::basic_string<>Varianten .

(C ++ 11 Hinweis: std::u16stringund std::u32stringsind besser , aber immer noch nicht perfekt. C ++ 20 gebracht std::u8string, aber alles, was Sie tun, ist die Codierung anzugeben. In vielerlei Hinsicht bleiben sie immer noch unwissend über Unicode-Mechanik, wie Normalisierung, Kollatierung, .. .)

Während Boost in Bezug auf die API gut aussieht , ist Boost.Locale im Grunde ein Wrapper um die Intensivstation . Wenn Boost mit ICU-Unterstützung kompiliert wird, ist Boost.Locale auf die für die Standardbibliothek kompilierte Gebietsschema-Unterstützung beschränkt.

Und glauben Sie mich, immer Boost zu kompilieren mit ICU manchmal einen echten Schmerzen sein kann. (Es gibt keine vorkompilierten Binärdateien für Windows, daher müssten Sie sie zusammen mit Ihrer Anwendung bereitstellen, und das öffnet eine ganz neue Dose Würmer ...)

Daher würde ich persönlich empfehlen, die volle Unicode-Unterstützung direkt aus dem Maul des Pferdes zu erhalten und die Intensivbibliothek direkt zu nutzen:

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

Kompilieren (in diesem Beispiel mit G ++):

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

Das gibt:

ὀδυσσεύς

Beachten Sie, dass die Σ <-> σ-Konvertierung in der Mitte des Wortes und die Σ <-> ς-Konvertierung am Ende des Wortes. Keine <algorithm>basierende Lösung kann Ihnen das geben.


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


19
Dies ist im allgemeinen Fall die richtige Antwort. Der Standard gibt nichts für den Umgang mit etwas anderem als "ASCII" außer Lügen und Täuschung. Es lässt Sie denken, dass Sie vielleicht mit vielleicht UTF-16 umgehen können, aber Sie können nicht. Wie diese Antwort sagt, können Sie nicht die richtige Zeichenlänge (nicht Bytelänge) einer UTF-16-Zeichenfolge erhalten, ohne Ihre eigene Unicode-Behandlung durchzuführen. Wenn Sie mit echtem Text arbeiten müssen, verwenden Sie die Intensivstation. Vielen Dank, @ DevSolar
Limited Atonement

Ist die Intensivstation unter Ubuntu / Windows standardmäßig verfügbar oder muss sie separat installiert werden? Wie wäre es auch mit dieser Antwort: stackoverflow.com/a/35075839/207661 ?
Shital Shah

1
Hey, schau mal, eine echte Antwort! Vielen Dank, dass Sie mich direkt auf den richtigen Weg gebracht haben, DevSolar.
Dan Bechard

2
@ DevSolar Einverstanden! Das Konzept der Länge ist im Text ziemlich bedeutungslos (wir könnten der Liste der Täter Ligaturen hinzufügen). Das heißt, da die Benutzer daran gewöhnt sind, Zeichen zu steuern und zu steuern, die eine Längeneinheit einnehmen, wären Codepunkte die intuitivere Maßnahme. Oh, und danke, dass du die richtige Antwort gegeben hast, traurig, dass du sie so weit unten gesehen
hast

3
@LF geringfügig besser. Aber so viele Dinge werden immer noch nicht behandelt: toupperund arbeiten tolowerimmer noch an einzelnen Charakteren. Die String-Klasse hat immer noch keine Vorstellung von Normalisierung (z. B. ob ein "ü" als "u mit Diaeresis" oder "u + kombinierte Diaeresis" codiert ist) oder wo ein String getrennt werden kann oder nicht. Die Liste geht weiter. u8string ist (wie die anderen Standard-String-Klassen) für "Durchlaufen" geeignet. Wenn Sie jedoch Unicode verarbeiten möchten , benötigen Sie eine Intensivstation.
DevSolar

36

Bei Verwendung einer bereichsbasierten for-Schleife von 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);
}

9
Auf einem französischen Computer konvertiert dieses Programm jedoch keine Nicht-ASCII-Zeichen, die in der französischen Sprache zulässig sind. Zum Beispiel eine Zeichenfolge 'Test String123. É Ï \ n 'wird konvertiert in:' test string123. É Ï \ n ', obwohl die Zeichen É Ï und ihre Kleinbuchstaben' é 'und' ï 'auf Französisch erlaubt sind. Es scheint, dass keine Lösung dafür durch andere Nachrichten dieses Threads bereitgestellt wurde.
schneidet

Ich denke, Sie müssen dafür ein geeignetes Gebietsschema festlegen.
user1095108

@incises, dann hat jemand eine Antwort über die Intensivstation gepostet und das ist sicherlich der richtige Weg. Einfacher als die meisten anderen Lösungen, die versuchen würden, das Gebietsschema zu verstehen.
Alexis Wilke

Ich persönlich würde es vorziehen, wenn möglich keine externen Bibliotheken zu verwenden.
KayleeFrye_onDeck


15

Dies ist eine Folge der Antwort von Stefan Mai: Wenn Sie das Ergebnis der Konvertierung in eine andere Zeichenfolge einfügen möchten, müssen Sie den Speicherplatz vor dem Aufruf vorab zuweisen std::transform. Da STL transformierte Zeichen im Zieliterator speichert (bei jeder Iteration der Schleife inkrementiert), wird die Größe der Zielzeichenfolge nicht automatisch geändert, und Sie riskieren ein Stomping des Speichers.

#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
Dies hat die Größe von Ä für mich nicht in ä
geändert

Könnte hier auch einen Back-Inserter-Iterator anstelle der manuellen Größenänderung verwenden.
Chili

11

Ein anderer Ansatz, der eine auf einem Bereich basierende Schleife mit Referenzvariable verwendet

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

cout<<test<<endl;

6

Soweit ich sehe, sind Boost-Bibliotheken in Bezug auf die Leistung wirklich schlecht. Ich habe ihre unordered_map auf STL getestet und sie war durchschnittlich dreimal langsamer (bester Fall 2, schlechtester zehnmal). Auch dieser Algorithmus sieht zu niedrig aus.

Der Unterschied ist so groß, dass ich mir sicher bin, dass jede Ergänzung, die Sie tun müssen, tolowerum den Boost "für Ihre Bedürfnisse" gleichzusetzen, viel schneller als der Boost sein wird.

Ich habe diese Tests auf einem Amazon EC2 durchgeführt, daher variierte 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 machte es so:

./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 einem dedizierten Computer durchführen, aber ich werde diesen EC2 verwenden, damit ich ihn nicht wirklich auf meinem Computer testen muss.


1
Haben Sie die Optimierungsoptionen beim Kompilieren geöffnet? Ich denke, die STL Heavy Boost-Bibliothek sollte mit hohem Optimierungsgrad besser laufen.
Wei Song

1
Ich habe -O2 in einem der Tests verwendet und sonst nichts.
Etherealone

2
Die Leistung von unordered_map hängt vom Hashing-Algorithmus ab, der mit den von Ihnen verwendeten Daten kombiniert wird. Es gibt keinen magischen Hashing-Algorithmus, der für alle und alle Daten funktioniert, um die unordered_map so schnell wie möglich zu machen. Benchmarking und verschiedene Dinge ausprobieren. Der Grund dafür, dass Sie eine schlechtere Leistung erzielen, ist, dass Sie mit dem von Ihnen verwendeten Hash viele Kollisionen erhalten, was im Grunde zu einer Suche in einer Liste führt. Weitere Informationen finden Sie auf dieser Website: fgda.pl/post/7/gcc-hash-map-vs-unordered-map Für meine Zwecke reduzierte die auf dem Link bereitgestellte Funktion Kollisionen und war daher sehr schnell.
leetNightshade

6

Der einfachste Weg, einen String in Kleinbuchstaben umzuwandeln, ohne sich um den Standard-Namespace zu kümmern, ist der folgende

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

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

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

Schön, solange Sie die Zeichen an Ort und Stelle konvertieren können. Was ist, wenn Ihre Quellzeichenfolge ist const? Das scheint es etwas chaotischer zu machen (z. B. sieht es nicht so aus, als könnten Sie es verwenden f.tolower()), da Sie die Zeichen in eine neue Zeichenfolge einfügen müssen. Würden Sie transform()und so etwas std::bind1st( std::mem_fun() )für den Betreiber verwenden?
Quazar

Für eine const-Zeichenfolge können wir einfach eine lokale Kopie erstellen und diese dann an Ort und Stelle konvertieren.
Sameer

Ja, das Erstellen einer Kopie erhöht jedoch den Overhead.
Quazar

Sie können std :: transform mit der Version von ctype :: tolower verwenden, die keine Zeiger akzeptiert. Verwenden Sie einen Back-Inserter-Iterator-Adapter, und Sie müssen sich nicht einmal um die Größenänderung Ihrer Ausgabezeichenfolge kümmern.
Chili

Großartig, insbesondere weil in libstdc ++ tolowermit localeParametern der implizite Aufruf von use_facetein Leistungsengpass zu sein scheint. Einer meiner Mitarbeiter hat eine Geschwindigkeitssteigerung von mehreren 100% erreicht, indem er boost::iequals(was dieses Problem hat) durch eine Version ersetzt use_facethat, die nur einmal außerhalb der Schleife aufgerufen wird.
Arne Vogel

3

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

POCO bietet zwei Varianten:

  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 unten 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

Es gibt eine Möglichkeit, Großbuchstaben in Kleinbuchstaben umzuwandeln, OHNE Tests durchzuführen , und dies ist ziemlich einfach. Die Verwendung von clocale.h durch die Funktion / das Makro von isupper () sollte sich um Probleme in Bezug auf Ihren Standort kümmern. Wenn nicht, können Sie das UtoL [] jederzeit nach Herzenslust anpassen.

Da die Zeichen von C wirklich nur 8-Bit-Ints sind (wobei die breiten Zeichensätze im 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 Indizes für die Zeichen verwenden Konvertierungsarray.

Geben Sie den Array-Mitgliedern in Großbuchstaben anstelle einer 1-zu-1-Zuordnung die BYTE-Int-Werte für die Kleinbuchstaben. Hier finden Sie möglicherweise islower () und isupper () .

Geben Sie hier die Bildbeschreibung ein

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

Dieser Ansatz ermöglicht es Ihnen gleichzeitig, alle anderen Zeichen, die Sie ändern möchten, neu zuzuordnen.

Dieser Ansatz hat einen großen Vorteil, wenn er auf modernen Prozessoren ausgeführt wird. Es ist nicht erforderlich, eine Verzweigungsvorhersage durchzuführen, da es keine If-Tests gibt, die eine Verzweigung umfassen. Dies speichert die Verzweigungsvorhersagelogik der CPU für andere Schleifen und verhindert tendenziell ein Abwürgen der Pipeline.

Einige hier erkennen diesen Ansatz möglicherweise als denselben an, der zum Konvertieren von EBCDIC in ASCII verwendet wird.


2
"Es gibt eine Möglichkeit, Großbuchstaben in Kleinbuchstaben umzuwandeln, OHNE dies zu tun, wenn Tests" jemals von Nachschlagetabellen gehört haben?
Gábor Buella

1
Undefiniertes Verhalten für negative Zeichen.
Roland Illig

Moderne CPUs haben einen Engpass im Speicher, nicht in der CPU. Benchmarking wäre interessant.
Contango

3

Da in keiner der Antworten die bevorstehende Ranges-Bibliothek erwähnt wurde, die seit C ++ 20 in der Standardbibliothek verfügbar ist und derzeit separat auf GitHub als verfügbar ist range-v3, möchte ich eine Möglichkeit hinzufügen, diese Konvertierung mit ihr 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.)

Hinweis: Die Verwendung unsigned charals Argument für das Lambda ist von cppreference inspiriert , in dem es heißt:

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

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 Werttyp des Iterators charoder ist signed char. Konvertieren Sie stattdessen den Wert in unsigned charfirst:

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

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

Das brauchte ich. Ich habe gerade das towlowerfür breite Zeichen verwendet, das UTF-16 unterstützt.
28.

2

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 der Kommentar von @ AndreasSpindler zu dieser Antwort immer noch eine wichtige Überlegung ist, wenn Sie an etwas arbeiten, das nicht nur aus ASCII-Zeichen besteht.


1
Ich stimme dem zu, weil ich Makros gebe, wenn es eine vollkommen gute Lösung gibt - Sie geben sogar diese Lösungen.
Klarer

2
Die Makrotechnik bedeutet weniger Eingabe von Code für etwas, das man normalerweise häufig beim Programmieren verwendet. Warum nicht das benutzen? Warum sonst überhaupt Makros?
Volomike

3
Makros sind ein Vermächtnis von C, an dem hart gearbeitet wird, um es loszuwerden. Wenn Sie die Eingabe reduzieren möchten, verwenden Sie eine Funktion oder ein Lambda. void strtoupper(std::string& x) { std::transform (x.begin(), x.end(), x.begin(), ::toupper); }
Klarer

1
@Clearer Da ich ein besserer Programmierer sein möchte, können Sie mir ANSI-Dokumentlinks zur Verfügung stellen, in denen ANSI C ++ - Komitees etwas sagen: "Wir müssen ein Meeting einberufen, um Makros aus C ++ zu entfernen"? Oder eine andere Roadmap?
Volomike

2
Nein, ich kann nicht. Bjarnes Haltung zu diesem Thema wurde jedoch mehrfach ziemlich deutlich. Außerdem gibt es viele Gründe, Makros nicht sowohl in C als auch in C ++ zu verwenden. xkönnte ein gültiger Ausdruck sein, der zufällig korrekt kompiliert wird, aber aufgrund der Makros völlig falsche Ergebnisse liefert.
Klarer

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

Für weitere Informationen: http://www.cplusplus.com/reference/locale/tolower/


2

Gibt es eine Alternative, die 100% der Zeit funktioniert?

Nein

Es gibt mehrere Fragen, die Sie sich stellen müssen, bevor Sie eine Kleinbuchstabenmethode auswählen.

  1. Wie ist die Zeichenfolge codiert? einfaches ASCII? UTF-8? irgendeine Form der erweiterten ASCII-Legacy-Codierung?
  2. Was meinst du überhaupt mit Kleinbuchstaben? Die Regeln für die Fallzuordnung variieren zwischen den Sprachen! Möchten Sie etwas, das im Gebietsschema des Benutzers lokalisiert ist? Möchten Sie etwas, das sich auf allen Systemen, auf denen Ihre Software ausgeführt wird, konsistent verhält? Möchten Sie nur ASCII-Zeichen in Kleinbuchstaben schreiben und alles andere durchlaufen?
  3. Welche Bibliotheken stehen zur Verfügung?

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


2

Probieren Sie diese Funktion aus :)

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

Auf Microsoft-Plattformen können Sie die strlwrFunktionsfamilie 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

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

Kopieren, da die Antwort nicht verbessert werden durfte. Danke SO


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

Erläuterung:

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 Auto- Bezeichner für den automatischen Typabzug verwendet. Der Typ wird also vom Variableninitialisierer abgezogen.

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

Die Zeichen der Zeichenfolge teststehen als Referenz innerhalb der for-Schleife durch c.


Bitte klären Sie, woher Sie Ihre Antwort kopiert haben.
Bfontaine

0

In C ++ sind keine Tolower- oder Toupper-Methoden für Zeichenfolgen implementiert, sie sind jedoch für char verfügbar. Man kann jedes Zeichen einer Zeichenfolge leicht lesen, in die gewünschte Groß- und Kleinschreibung konvertieren und wieder in eine Zeichenfolge einfügen. Ein Beispielcode ohne Verwendung einer Bibliothek eines Drittanbieters:

#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 an Zeichenfolgen: Für jedes Zeichen in Zeichenfolgen


-1

Dies könnte eine weitere einfache Version sein, um Großbuchstaben in Kleinbuchstaben umzuwandeln und umgekehrt. Ich habe die VS2017-Community-Version 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 diese mithilfe der Bedingungsprüfung behandelt werden.


-8

Ich habe versucht, std :: transform, alles was ich bekomme ist ein abscheulicher stl criptic Kompilierungsfehler, den nur Druiden von vor 200 Jahren verstehen können (kann nicht von zu Flibidi Flabidi Grippe konvertieren)

Dies funktioniert gut und kann leicht angepasst werden

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;
}
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.