144 votes

Quelle est l'idée derrière ^= 32, qui convertit les lettres minuscules en majuscules et vice versa ?

Je résolvais un problème sur Codeforces. Normalement, je vérifie d'abord si le caractère est une lettre anglaise supérieure ou inférieure, puis je soustrais ou j'ajoute 32 pour le convertir en la lettre correspondante. Mais j'ai trouvé quelqu'un qui fait ^= 32 pour faire la même chose. C'est ici :

char foo = 'a';
foo ^= 32;
char bar = 'A';
bar ^= 32;
cout << foo << ' ' << bar << '\n'; // foo is A, and bar is a

J'ai cherché une explication à cela et je ne l'ai pas trouvée. Alors pourquoi cela fonctionne-t-il ?

5 votes

fr.wikipedia.org/wiki/File:USASCII_code_chart.png Conseil : vous pouvez convertir @ en ` en utilisant ^ 32 .

112 votes

Pour information, cela ne "fonctionne" pas vraiment. Il fonctionne pour ce jeu de caractères particulier, mais il y a d'autres jeux pour lesquels il ne fonctionne pas. toupper y tolower pour changer d'affaire.

7 votes

Parfois, avec les concours en ligne, "l'idée" est d'écrire du code d'une manière tellement obscure qu'il ne passerait jamais un examen sérieux ;)

147voto

Hanjoung Lee Points 1923

Jetons un coup d'œil à la table de code ASCII en binaire.

A 1000001    a 1100001
B 1000010    b 1100010
C 1000011    c 1100011
...
Z 1011010    z 1111010

Et 32 est 0100000 ce qui est la seule différence entre les lettres minuscules et majuscules. Donc, en changeant ce bit, on change la casse d'une lettre.

49 votes

"bascule la casse" *seulement pour ASCII

39 votes

@Mooing uniquement pour A-Za-z en ASCII. La minuscule de "[" est no "{".

21 votes

@dbkk { est plus courte que [ donc c'est une "minuscule". Non ? Ok, je vais me montrer :D

116voto

YSC Points 3386

Cela utilise le fait que les valeurs ASCII ont été choisies par des personnes très intelligentes.

foo ^= 32;

Ce site retourne le 6ème bit le plus bas 1 de foo (le drapeau des majuscules de l'ASCII en quelque sorte), transformant une majuscule ASCII en minuscule et vice-versa .

+---+------------+------------+
|   | Upper case | Lower case |  32 is 00100000
+---+------------+------------+
| A | 01000001   | 01100001   |
| B | 01000010   | 01100010   |
|            ...              |
| Z | 01011010   | 01111010   |
+---+------------+------------+

Ejemplo

'A' ^ 32

    01000001 'A'
XOR 00100000 32
------------
    01100001 'a'

Et par la propriété de XOR, 'a' ^ 32 == 'A' .

Avis

Le C++ n'est pas tenu d'utiliser l'ASCII pour représenter les caractères. Une autre variante est EBCDIC . Cette astuce ne fonctionne que sur les plateformes ASCII. Une solution plus portable serait d'utiliser std::tolower y std::toupper avec l'avantage d'être adapté aux spécificités locales (il ne résout pas automatiquement tous vos problèmes, voir les commentaires) :

bool case_incensitive_equal(char lhs, char rhs)
{
    return std::tolower(lhs, std::locale{}) == std::tolower(rhs, std::locale{}); // std::locale{} optional, enable locale-awarness
}

assert(case_incensitive_equal('A', 'a'));

1) Comme 32 est 1 << 5 (2 à la puissance 5), il retourne le 6ème bit (en comptant à partir de 1).

16 votes

L'EBCDIC a également été choisi par des personnes très intelligentes : il fonctionne très bien sur les cartes perforées, contrairement à l'ASCII qui est un véritable gâchis. Mais c'est une belle réponse, +1.

1 votes

@Bathsheba ASCII sur carte perforée ? Qui le ferait ? :D

65 votes

Je ne sais pas pour les cartes perforées, mais l'ASCII était utilisé sur le ruban de papier. C'est pourquoi le caractère "Supprimer" est codé 1111111 : vous pouvez donc marquer n'importe quel caractère comme "supprimé" en perçant tous les trous de sa colonne sur la bande.

34voto

Damon Points 26437

Permettez-moi de dire que c'est - bien que cela semble intelligent - un hack vraiment, vraiment stupide. Si quelqu'un vous recommande cela en 2019, frappez-le. Frappez-le aussi fort que vous le pouvez.
Vous pouvez, bien sûr, le faire dans votre propre logiciel que vous et personne d'autre n'utilise si vous savez que vous n'utiliserez jamais d'autre langue que l'anglais de toute façon. Sinon, pas question.

On peut dire que le piratage était "correct" il y a 30 ou 35 ans, lorsque les ordinateurs ne faisaient pas grand-chose d'autre que de l'anglais en ASCII. peut-être une ou deux grandes langues européennes. Mais... ce n'est plus le cas.

Le hack fonctionne parce que les majuscules et minuscules US-Latin sont exactement 0x20 l'un de l'autre et apparaissent dans le même ordre, ce qui n'est qu'une petite différence. Qui, en fait, ce bit hack, bascule.

Les personnes qui ont créé les pages de code pour l'Europe occidentale, et plus tard le consortium Unicode, ont été assez intelligentes pour conserver ce schéma pour les trémas allemands et les voyelles à accent français. Ce n'est pas le cas pour le ß qui (jusqu'à ce que quelqu'un convainque le consortium Unicode en 2017, et qu'un grand magazine de presse écrite Fake News écrive à ce sujet, convainquant en fait le Duden - pas de commentaire à ce sujet). n'existent même pas comme un versal (se transforme en SS). Maintenant, il fait existent en tant que versal, mais les deux sont 0x1DBF positions séparées, pas 0x20 .

Les exécutants, eux, l'étaient, no assez prévenants pour continuer à le faire. Par exemple, si vous appliquez votre hack dans certaines langues d'Europe de l'Est ou autres (je ne connais pas le cyrillique), vous aurez une mauvaise surprise. Tous ces caractères "hatchet" en sont des exemples, les minuscules et les majuscules sont à part. Le hack fait donc no fonctionnent correctement à cet endroit.

Il y a bien d'autres choses à prendre en compte, par exemple, certains caractères ne passent pas simplement des minuscules aux majuscules (ils sont remplacés par des séquences différentes), ou ils peuvent changer de forme (nécessitant des points de code différents).

Ne pensez même pas à ce que ce piratage fera à des choses comme le thaï ou le chinois (cela vous donnera juste un non-sens total).

L'économie de quelques centaines de cycles CPU était peut-être très intéressante il y a 30 ans, mais de nos jours, il n'y a vraiment aucune excuse pour convertir correctement une chaîne de caractères. Il existe des fonctions de bibliothèque pour effectuer cette tâche non triviale.
Le temps nécessaire pour convertir plusieurs dizaines de kilo-octets de texte correctement est négligeable de nos jours.

2 votes

Je suis tout à fait d'accord - bien que ce soit une bonne idée pour chaque programmeur de savoir pourquoi cela fonctionne - cela pourrait même faire une bonne question d'entretien Qu'est-ce que cela fait et quand faut-il l'utiliser :)

32voto

Jack Aidley Points 3993

Cela fonctionne car la différence entre "a" et "A" en ASCII et dans les codages dérivés est de 32, et 32 est également la valeur du sixième bit. En inversant le 6e bit avec un OU exclusif, on peut donc convertir les valeurs supérieures et inférieures.

21voto

Blaze Points 16456

Le plus souvent, votre implémentation du jeu de caractères sera ASCII. Si nous regardons le tableau :

enter image description here

On voit qu'il y a une différence d'exactement 32 entre la valeur d'un chiffre en minuscule et en majuscule. Par conséquent, si nous faisons ^= 32 (ce qui équivaut à basculer le 6ème bit le moins significatif), il passe d'un caractère minuscule à un caractère majuscule.

Notez qu'elle fonctionne avec tous les symboles, et pas seulement avec les lettres. Elle fait basculer un caractère avec le caractère correspondant dont le 6e bit est différent, ce qui donne une paire de caractères entre lesquels on bascule. Pour les lettres, les caractères majuscules/minuscules respectifs forment une telle paire. A NUL se transformera en Space et dans l'autre sens, et le @ bascule avec le backtick. En fait, tout caractère de la première colonne de ce graphique bascule avec le caractère de la colonne suivante, et il en va de même pour les troisième et quatrième colonnes.

Je n'utiliserais cependant pas ce hack, car il n'est pas garanti qu'il fonctionne sur tous les systèmes. Utilisez simplement toupie y tolérer à la place, et des requêtes telles que isupper .

2 votes

Eh bien, ça ne fonctionne pas pour toutes les lettres qui ont une différence de 32. Sinon, cela fonctionnerait entre '@' et ' ' !

2 votes

@MatthieuBrucher Cela fonctionne, 32 ^ 32 est 0, et non 64

0 votes

@NathanOliver Oui, c'est ce que je veux dire, cela ne fonctionne pas entre deux caractères séparés par 32, seulement ceux qui ont un motif spécifique entre eux.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X