Was ist die Idee hinter ^= 32, wandelt Kleinbuchstaben Ober-und Umgekehrt?
War ich der Lösung einiger problem auf codeforces. Normalerweise bin ich erstmal prüfen, ob das Zeichen ist die Obere oder untere Englisch schreiben, dann subtrahieren oder fügen Sie 32
zu konvertieren, um den entsprechenden Buchstaben. Aber ich fand jemand ^= 32
um das gleiche zu tun. Hier ist es:
char foo = 'a';
foo ^= 32;
char bar = 'A';
bar ^= 32;
cout << foo << ' ' << bar << '\n'; //foo is A, and bar is a
Suchte ich nach einer Erklärung für diese und nicht herausfinden. Also, warum das funktioniert?
- en.wikipedia.org/wiki/File:USASCII_code_chart.png Tipp: Sie können konvertieren
@
in " mit^ 32
. - FWIW, es ist nicht wirklich "Arbeit". Es funktioniert für dieses Besondere Zeichen gesetzt, aber es gibt andere sets, wenn es nicht, sollten Sie Sie verwenden
toupper
undtolower
zu wechseln, Fällen. - irgendwann mit online-Gewinnspiele "die Idee" ist, code zu schreiben, bei solch einer verwirrenden Weise, dass es nie eine ernsthafte überprüfung 😉
- ^= ist die Umwandlung der Wert mit XOR. Großbuchstaben ASCII-Buchstaben haben eine null in das entsprechende bit, während Kleinbuchstaben ein. Das heißt, bitte nicht! Verwenden Sie die richtigen Zeichen (unicode -) Routinen zur Konvertierung zwischen klein-und Großschreibung. Die ära der nur ASCII ist lange vorbei.
- was @NathanOliver sagte. Das ist nett, aber sein gehen, verursachen einen Fehler.
- Es ist nicht nur, dass es funktioniert nur mit manchen Zeichensätzen. Selbst wenn wir annehmen, dass alle Welt UTF-8 (das könnte zumindest eine nette utopische Ziel), es funktioniert darüber hinaus nur mit den 26 Buchstaben
A
zuZ
. Das ist in Ordnung, solange Sie nur die Sorge um Deutsch (und nicht verwenden, Schreibweisen von "naiv", Wörter wie "café" oder Namen mit diakritischen Zeichen...), aber die Welt ist nicht nur Englisch. - Verwandte: Sie können prüfen, ob Sie eine alphabetische ASCII-Zeichen durch erzwingen Kleinbuchstaben mit
|= 0x20
und dann die Kontrolle (unsigned)if(c - 'a' < ('a'-'z'))
. Also nur 3 Operationen: ODER + SUB + CMP. Siehe auch Wandelt einen String In C++ - groß SIMD (string toupper Maskierung der operand XOR) und Wie auf ein char-array und ändere Kleinbuchstaben in Großbuchstaben und Umgekehrt (C mit SIMD-Interna, und skalaren x86-asm-Fall-Abdeckung für alphabetische Zeichen, so dass andere unverändert.) - Auch wenn [sehr hypothetisch] das war weniger empfindlich und haben mehr Charakter-sets, etc., Ich würde noch raten es zu benutzen. Es sind standard-Möglichkeiten, um dies zu tun, so versuchen, um die Vorteile der Werkzeuge, die bereits existieren. Erfinden Sie Ihre eigene "clevere" Art und Weise zu tun, es ist eine großartige Möglichkeit, um am Ende mit nicht lesbar/wartbaren code, das ist wirklich schwer zu Debuggen, wenn Sie schließlich feststellen, dass der Fall ist, bricht Ihre Umsetzung. (Nur ein allgemeiner Kommentar und nicht ein Angriff auf die OP, die wirklich nur gefragt warum das funktioniert)
- Ich kann nicht sagen, von der Frage, ob Sie wissen, was die
^
und^=
Betreiber in den ersten Platz, aber es ist relevant für die Beantwortung Ihrer Frage. Tun Sie? - Nebenbei die XOR/ODER usw. ist eine sehr verbreitete Methode für EBCDIC Fall das wechseln und hantieren. Ich würde nicht versuchen, diese mit DBCS noch Unicode. Umwandlung in " int " für arithmetische weitgehend in Ungnade gefallen, für viele gute Gründe.
- Im follow-up an @NathanOliver Kommentar - Dinge, die zu funktionieren scheinen für einen Teil der Dinge, aber nicht behaupten, dass Sie nur vermittelt bekommen, dass die Teilmenge ist betteln für ein Fehler in der Zukunft ... und du wirst es nie erfahren. Wenn Sie nicht möchten, zu verwenden, std::toupper/tolower (weil vielleicht Ihrer Plattform nicht bieten die std-Bibliothek), dann sollten Sie zumindest geltend machen, dass Sie suchen, bei [a-zA-Z]
- TBH
toupper
undtolower
sind hoffnungslos gebrochen in alle multibyte-Codierung, wie die ach-so-selten-verwendet UTF-8. Es wäre vielleicht eine Lösung, vielleicht in den 80er Jahren, aber heute würde ich argumentieren, ist wahrscheinlich noch schlimmer als^32
. - Mögliche Duplikate von Wie geht s[i]^=32 konvertieren oberen bis zum unteren Fall?
- Ich habe immer gewusst, dass dieser trick als
^= ' '
- Der "trick", den ich verwendet wurde, war
^= 'A' ^ 'a'
wenntoupper()
war nicht verfügbar und benötigt enge code. - Es funktioniert einfach für die standard-ASCII-Tabelle, wobei der Abstand 32 zwischen groß-Buchstaben und einem Kleinbuchstaben Alphabete. Es berücksichtigt nicht die locale oder die erweiterte ASCII-Tabelle. In der grundlegenden Tabelle, können Sie diese verwenden, um zu gehen von e zu e oder Umgekehrt. Aber in der locale fr_FR, alle Varianten des e zuzuordnen sind E-Mail, wenn groß geschrieben, aber in fr_CA, die Akzente bleiben. Das bedeutet, dass Großbuchstaben("eéèëê") ==> "EEEEE" in fr_FR ==> "EÉÈËÊ" in fr_CA.
Du musst angemeldet sein, um einen Kommentar abzugeben.
Werfen wir einen Blick auf die ASCII-code-Tabelle in Binär.
Und 32 ist
0100000
das ist der einzige Unterschied zwischen klein-und Großbuchstaben. So Umschalten, dass etwas schaltet Falle eines Briefes.[
, so ist es ein "niedriger" der Fall. Nein? Ok, ich zeige mich aus 😀foobar[]
undfoobar{}
zu identischen Namen, als Spitznamen sind case unempfindlich, und IRC hat seinen Ursprung in Skandinavien 🙂Diese nutzt die Tatsache als ASCII-Werte gewählt wurden, die von wirklich intelligenten Menschen.
Diese kippt der 6. niedrigste bit1 von
foo
(die groß-flag ASCII-Art), die Umwandlung eines ASCII-Großbuchstaben, einen Kleinbuchstaben und Umgekehrt.Beispiel
Und durch die Eigenschaft der XOR -,
'a' ^ 32 == 'A'
.Bemerken
C++ ist nicht erforderlich, verwenden ASCII zum darstellen von Zeichen. Eine andere Variante ist EBCDIC. Dieser trick funktioniert nur auf ASCII-Plattformen. Eine weitere tragbare Lösung wäre die Verwendung
std::tolower
undstd::toupper
, mit den angebotenen bonus zu locale-aware (es werden nicht automatisch alle Ihre Probleme lösen, obwohl, siehe Kommentare):1) Als 32 ist
1 << 5
(2 hoch 5), es spiegelt das 6. bit (Zählung ab 1).ß
undSS
.^= 64
arbeiten für EBCDIC-aber! (Aber nicht für ASCII mehr)tolower
im deutschen nicht nur Wörterbuch es muss in der Lage sein, um eine Analyse der Bedeutung.std::tolower()
ist nur definiert fürEOF
und Argumente in derunsigned char
-Bereich?std::tolower (<cctype>)
, denke ich. Diese Antwort ist über diestd::tolower (<clocale>)
.Erlauben Sie mir zu sagen, dass dies-obwohl es scheint, schlau-ein wirklich, wirklich dummen hack. Wenn jemand empfiehlt Ihnen in 2019, traf ihn. Schlagen Sie ihn so hart wie Sie können.
Sie können, natürlich, tun Sie es in Ihre eigene software, die Sie und sonst niemand nutzt, wenn Sie wissen, dass Sie nie jede Sprache, aber Englisch sowieso. Ansonsten no go.
Der hack war wohl "OK", einige von 30-35 Jahren, wenn die Computer nicht wirklich viel tun, aber Englisch in ASCII und vielleicht eine oder zwei große Europäische Sprachen. Aber... nicht mehr so.
Der hack funktioniert, weil US-lateinischen groß - und Kleinbuchstaben sind genau
0x20
voneinander getrennt und in der gleichen Reihenfolge angezeigt, die nur ein bit Unterschied. Die in der Tat, das bisschen hack, schaltet.Nun, die Menschen, das erstellen von code-Seiten für Westeuropa und später auch das Unicode-Konsortium, waren klug genug, um zu halten diese Regelung für z.B. die deutschen Umlaute und die französischen akzentuierten Vokale. Nicht so für die ß das (bis jemand davon überzeugt das Unicode-Konsortium im Jahr 2017, und einem großen Fake-Nachrichten print-Magazin über ihn geschrieben hat, tatsächlich überzeugen die Duden-kein Kommentar) gar nicht existieren als versal (Transformationen zu SS). Jetzt ist es hat existieren als versal, aber die beiden sind
0x1DBF
Positionen auseinander, nicht0x20
.Den implementors wurden, jedoch nicht rücksichtsvoll genug, um diese gehen. Zum Beispiel, wenn Sie Ihre hacken in einigen Ost-europäischen Sprachen und der wie (ich möchte nicht wissen Kyrillisch), erhalten Sie eine böse überraschung. Alle diejenigen, die "hatchet" - Zeichen sind Beispiele dafür, dass, Kleinbuchstaben und Großbuchstaben sind eine Ausnahme. Der hack so funktioniert nicht richtig dort arbeiten.
Gibt es viel mehr zu berücksichtigen, zum Beispiel, einige Zeichen, die nicht einfach zu transformieren-von unten in Großbuchstaben auf allen (Sie sind ersetzt mit unterschiedlichen Sequenzen), oder Sie kann sich ändern, form (mit unterschiedlichen code-Punkte).
Gar nicht darüber nachdenken, was dieser hack tun, um Sachen wie Thai oder Chinesisch (es werden Ihnen nur völliger Unsinn ist).
Speichern von ein paar hundert CPU-Zyklen können sehr lohnend, vor 30 Jahren, aber heutzutage gibt es wirklich keine Entschuldigung für die Konvertierung einer Zeichenfolge ordnungsgemäß. Es sind library-Funktionen für die Durchführung dieser nicht-triviale Aufgabe.
Die Zeit, die zum konvertieren von mehreren Dutzend kilobytes von text richtig ist heutzutage vernachlässigbar.
Es funktioniert, weil, wie es geschieht, der Unterschied zwischen 'a' und A' in ASCII und abgeleitete Kodierungen ist 32, und 32 ist auch der Wert der sechsten bit. Flipping das 6. bit, die mit einem exklusiv-ODER-so wandelt zwischen Ober-und Unterteil.
Wahrscheinlich Ihre Umsetzung der Zeichensatz ASCII-Zeichensatz sein. Wenn wir den Blick auf die Tabelle:
Sehen wir, dass es eine Differenz von genau
32
zwischen dem Wert eines klein-und Großschreibung-Nummer. Daher, wenn wir^= 32
(das entspricht Umschalten des 6. least significant bit), wechselt er zwischen Kleinbuchstaben und Großbuchstaben enthalten.Beachten Sie, dass es funktioniert mit allen Symbolen, nicht nur die Buchstaben. Es schaltet Sie ein Zeichen mit dem jeweiligen Charakter, wo das 6. bit ist anders, was in ein paar von Zeichen, die ein-und ausgeschaltet hin und her zwischen. Für die Briefe, die jeweilige groß - /Kleinschreibung Zeichen bilden ein solches paar. Ein
NUL
wird sich ändern inSpace
und die andere Weise herum, und die@
schaltet mit der "backtick". Im Grunde alle Zeichen in der ersten Spalte auf dieser chart wechselt mit dem Charakter einer Spalte über, und das gleiche gilt für den Dritten und vierten Spalten.Ich würde nicht verwenden Sie diese hack-obwohl, da gibt es keine Garantie, dass es funktioniert auf jedem system. Verwenden Sie einfach toupper und tolower statt, und Anfragen wie isupper.
32 ^ 32
ist 0, nicht 64@
mit dem umgekehrten Apostroph, nicht mit Raum. Jeder char ist ein Teil von einem paar, das ein-und ausgeschaltet hin und her zwischen. Vielleicht durch die Aufklärung, war unklar.[a-z]
und[A-Z]
sind "Briefe". Der rest sind Zufälle, Folgen die gleiche Regel. Wenn jemand Sie fragte, "upper case ]", was würde es sein? es würde noch das "]" - "}" ist das nicht der "Obere Fall" von "]".%32
"alignment" - Grenze in der ASCII-Codierung. Das ist der Grund, warum bit0x20
ist der einzige Unterschied zwischen der groß - /Kleinschreibung Versionen von den gleichen Brief. Wenn dies nicht der Fall war, würden Sie brauchen, um hinzuzufügen oder zu subtrahieren0x20
, nicht nur umzuschalten, und für einige Buchstaben gibt, würde es sein, durchführen zu flip-anderen-höherwertigen bits. (Und den gleichen Vorgang konnte nicht Umschalten, und das überprüfen auf alphabetische Zeichen in der ersten Stelle ist härter, weil du nicht|= 0x20
zu zwingen, lcase.)Viele gute Antworten hier, die beschreiben, wie das funktioniert, aber warum es funktioniert auf diese Art und Weise ist, um die Leistung zu verbessern. Bitweise Operationen sind schneller als die meisten anderen Operationen innerhalb eines Prozessors. Sie können schnell eine Kleinschreibung-Vergleich indem Sie einfach nicht auf der Suche auf das bit, das bestimmt, Fall oder ändern Sie den Fall zu den oberen/unteren einfach durch spiegeln des Bits (die Jungs, die entwickelt die ASCII-Tabelle waren ziemlich smart).
Offensichtlich, dies ist nicht annähernd so groß einen deal, wie es heute war im Jahre 1960 (als die erste Arbeit begann auf ASCII) durch schnellere Prozessoren und Unicode, aber es gibt immer noch einige low-cost-Prozessoren, könnte dies einen signifikanten Unterschied machen, solange Sie garantieren können, die nur ASCII-Zeichen.
https://en.wikipedia.org/wiki/Bitwise_operation
HINWEIS: ich würde empfehlen die Verwendung von standard-Bibliotheken für die Arbeit mit strings für eine Reihe von Gründen (Lesbarkeit, Richtigkeit, übertragbarkeit, etc.). Verwenden Sie nur bit-flipping, wenn du gemessen hast Leistung und das ist dein Flaschenhals.
Es ist wie ASCII funktioniert, das ist alles.
Aber in der Ausnutzung dieser, Sie geben Portabilität als C++ nicht darauf bestehen, ASCII als Zeichenkodierung.
Dies ist der Grund, warum die Funktionen
std::toupper
undstd::tolower
umgesetzt werden, die im C++ standard library - Sie sollten diese statt.Finden Sie in der zweiten Tabelle bei http://www.catb.org/esr/faqs/things-every-hacker-once-knew/#_ascii, und die folgenden Notizen, nachstehend wiedergegeben:
ASCII wurde so konzipiert, dass das shift und Strg Tastatur-Tasten, die umgesetzt werden könnten, ohne viel (oder vielleicht jede für Strg) Logik - - shift wahrscheinlich benötigt nur ein paar Tore. Es machte wohl mindestens so viel Sinn, um zu speichern das wire-Protokoll wie jedes andere Zeichen-Codierung (keine software-Umstellung erforderlich).
Verlinkten Artikel auch erklärt viele merkwürdige hacker-Konventionen wie
And control H does a single character and is an old^H^H^H^H^H classic joke.
(finden Sie hier).foo ^= (foo & 0x60) == 0x20 ? 0x10 : 0x20
, obwohl dies nur ASCII und daher unklug, aus den erklärten Gründen in anderen Antworten. Es kann wohl auch sein, verbesserte w/Zweig-freie Programmierung.foo ^= 0x20 >> !(foo & 0x40)
einfacher wäre. Auch ein gutes Beispiel dafür, warum knapp-code wird oft als unlesbar ^_^.Xoring mit 32 (00100000 im Binär -) setzt oder löscht das sechste bit (von rechts). Das ist streng äquivalent zu addieren oder subtrahieren 32.
Die Kleinbuchstaben und Großbuchstaben des Alphabets reicht nicht über eine
%32
"alignment" - Grenze in der ASCII-Codierung.Dies ist der Grund, warum bit
0x20
ist der einzige Unterschied zwischen der groß - /Kleinschreibung Versionen von den gleichen Brief.Wenn dies nicht der Fall war, würden Sie brauchen, um hinzuzufügen oder zu subtrahieren
0x20
, nicht nur umzuschalten, und für einige Buchstaben gibt, würde es sein, durchführen zu flip-anderen-höherwertigen bits. (Und es gibt nicht einen einzigen Vorgang, der könnte Umschalten, und das überprüfen auf alphabetische Zeichen in der ersten Stelle ist härter, weil du nicht |= 0x20 zu zwingen, lcase.)Zusammenhang nur ASCII-tricks: können Sie prüfen, ob Sie eine alphabetische ASCII-Zeichen durch erzwingen Kleinbuchstaben mit
c |= 0x20
und dann überprüfen, ob (unsigned)c - 'a' <= ('z'-'a')
. Also nur 3 Operationen: ODER + SUB + CMP mit einem Konstanten 25. Natürlich Compiler wissen, wie zu optimieren(c>='a' && c<='z')
in asm, wie dies für Sie, so bei den meisten, die Sie tun sollten, diec|=0x20
Teil selbst. Es ist ziemlich umständlich, alles zu tun, die notwendig casting selbst, vor allem arbeiten rund um Standard-integer-promotions unterzeichnetint
.Siehe auch Konvertieren einer Zeichenfolge In C++ - groß - SIMD (string
toupper
für nur ASCII, Maskierung der operand XOR mit, dass der check.)Und auch Wie auf ein char-array und ändere Kleinbuchstaben in Großbuchstaben und Umgekehrt
(C mit SIMD-Interna, und skalaren x86-asm-Fall-Abdeckung für alphabetische ASCII-Zeichen, so dass andere unverändert.)
Diese tricks sind meist nur nützlich, wenn Sie von hand optimieren Sie text-Verarbeitung mit SIMD (z.B. SSE2 oder NEON), nach überprüfung, dass keiner der
char
s in einen Vektor haben Ihre high-bit gesetzt. (Und so ist keiner der bytes sind Teil eines multi-byte-UTF-8-Codierung für ein einzelnes Zeichen, die möglicherweise unterschiedliche groß - /Kleinschreibung inversen). Wenn Sie alle finden, können Sie zurückgreifen, um Skalare für dieses Stück von 16 bytes, oder für den rest der Zeichenfolge.Es gibt auch einige Sprachen, wo
toupper()
odertolower()
auf einige Zeichen in der ASCII-Bereich produzieren Zeichen außerhalb dieses Bereichs, insbesondere türkischen, wo ich ↔ ı und I ↔ i. In diesen Gebietsschemas, brauchen Sie eine anspruchsvolle Prüfung, oder vielleicht nicht versucht, verwenden Sie diese Optimierung auf alle.Aber in einigen Fällen, Sie dürfen davon ausgehen, ASCII statt UTF-8, z.B. Unix-Dienstprogramme, die mit
LANG=C
(die POSIX-locale), nichten_CA.UTF-8
oder was auch immer.Aber wenn Sie Sie überprüfen können, sicher ist, können Sie
toupper
medium-length strings viel schneller als das aufrufentoupper()
in einer Schleife (wie 5x), und letzten habe ich getestet mit Boost-1.58, viel viel schneller alsboost::to_upper_copy<char*, std::string>()
die nicht dummdynamic_cast
für jeden Charakter.