strupr - funcion toupper c++
¿Cuál es la idea detrás de ^=32, que convierte las letras minúsculas en mayúsculas y viceversa? (10)
Así es como funciona ASCII, eso es todo.
Pero al explotar esto, está renunciando a la portabilidad ya que C ++ no insiste en que ASCII sea la codificación.
Esta es la razón por la cual las funciones
std::toupper
y
std::tolower
se implementan en la biblioteca estándar de C ++; en su lugar, debe usarlas.
Estaba resolviendo un problema en las fuerzas de código.
Normalmente primero verifico si el carácter es una letra inglesa superior o inferior, luego resto o agrego
32
para convertirlo en la letra correspondiente.
Pero encontré que alguien hace
^= 32
para hacer lo mismo.
Aquí está:
char foo = ''a'';
foo ^= 32;
char bar = ''A'';
bar ^= 32;
cout << foo << '' '' << bar << ''/n''; // foo is A, and bar is a
He buscado una explicación para esto y no lo descubrí. Entonces, ¿por qué esto funciona?
Consulte la segunda tabla en http://www.catb.org/esr/faqs/things-every-hacker-once-knew/#_ascii , y las siguientes notas, reproducidas a continuación:
El modificador de control en su teclado básicamente borra los tres bits superiores de cualquier carácter que escriba, dejando los cinco inferiores y asignándolos al rango de 0..31. Entonces, por ejemplo, Ctrl-SPACE, Ctrl- @ y Ctrl-`significan lo mismo: NUL.
Los teclados muy antiguos solían hacer Shift simplemente alternando los 32 o 16 bits, dependiendo de la clave; esta es la razón por la cual la relación entre minúsculas y mayúsculas en ASCII es tan regular, y la relación entre los números y los símbolos, y algunos pares de símbolos, es algo regular si lo analizas. El ASR-33, que era un terminal en mayúsculas, incluso le permite generar algunos caracteres de puntuación para los que no tenía claves al cambiar el 16 bit; así, por ejemplo, Shift-K (0x4B) se convirtió en un [(0x5B)
ASCII se diseñó de tal manera que las teclas shift y ctrl del teclado podrían implementarse sin mucha lógica (o quizás alguna para ctrl ); el cambio probablemente requería solo unas pocas puertas. Probablemente tenía tanto sentido almacenar el protocolo de conexión como cualquier otra codificación de caracteres (no se requiere conversión de software).
El artículo vinculado
también
explica muchas convenciones de hackers extrañas, como
And control H does a single character and is an old^H^H^H^H^H classic joke.
(
encontrado aquí
).
Echemos un vistazo a la tabla de códigos ASCII en binario.
A 1000001 a 1100001
B 1000010 b 1100010
C 1000011 c 1100011
...
Z 1011010 z 1111010
Y 32 es
0100000
que es la única diferencia entre mayúsculas y minúsculas.
Así que alternar ese bit alterna el caso de una letra.
Esto utiliza el hecho de que los valores ASCII han sido elegidos por personas realmente inteligentes.
foo ^= 32;
Esto
invierte el sexto bit
1
más bajo
de
foo
(el indicador en mayúsculas del tipo ASCII), transformando una mayúscula ASCII en minúscula y
viceversa
.
+---+------------+------------+
| | 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''
Y por propiedad de XOR,
''a'' ^ 32 == ''A''
.
darse cuenta
No se requiere que C ++ use ASCII para representar caracteres.
Otra variante es
EBCDIC
.
Este truco solo funciona en plataformas ASCII.
Una solución más portátil sería utilizar
std::tolower
y
std::toupper
, con el bono ofrecido para tener en cuenta la configuración regional (aunque no resuelve automáticamente todos sus problemas, vea los comentarios):
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)
Como 32 es
1 << 5
(2 a la potencia 5), cambia el sexto bit (contando desde 1).
Funciona porque, como sucede, la diferencia entre ''a'' y A ''en ASCII y las codificaciones derivadas es 32, y 32 también es el valor del sexto bit. Al voltear el sexto bit con un OR exclusivo, se convierte entre superior e inferior.
Lo más probable es que su implementación del conjunto de caracteres sea ASCII. Si nos fijamos en la mesa:
Vemos que hay una diferencia de exactamente
32
entre el valor de un número en minúscula y mayúscula.
Por lo tanto, si hacemos
^= 32
(lo que equivale a alternar el sexto bit menos significativo), cambia entre un carácter en minúscula y en mayúscula.
Tenga en cuenta que funciona con todos los símbolos, no solo con las letras.
Alterna un carácter con el carácter respectivo en el que el sexto bit es diferente, lo que da como resultado un par de caracteres que se alternan entre uno y otro.
Para las letras, los respectivos caracteres en mayúsculas / minúsculas forman dicho par.
Un
NUL
cambiará a
Space
y viceversa, y
@
cambiará con el backtick.
Básicamente, cualquier carácter en la primera columna en este gráfico alterna con el carácter una columna más, y lo mismo se aplica a la tercera y cuarta columnas.
Sin embargo, no usaría este truco, ya que no hay garantía de que vaya a funcionar en ningún sistema. Simplemente use toupper y tolower lugar, y consultas como isupper .
Muchas respuestas buenas aquí que describen cómo funciona esto, pero por qué funciona de esta manera es para mejorar el rendimiento. Las operaciones bitwise son más rápidas que la mayoría de las otras operaciones dentro de un procesador. Puede hacer rápidamente una comparación que no distinga mayúsculas de minúsculas simplemente no mirando la broca que determina el caso o cambiar la mayúscula a la inferior / superior simplemente al voltear la broca (aquellos que diseñaron la tabla ASCII fueron muy inteligentes).
Obviamente, esto no es un problema tan grande hoy como lo fue en 1960 (cuando se comenzó a trabajar en ASCII) debido a los procesadores más rápidos y Unicode, pero todavía hay algunos procesadores de bajo costo que podrían marcar una gran diferencia. Siempre y cuando pueda garantizar sólo caracteres ASCII.
https://en.wikipedia.org/wiki/Bitwise_operation
En los procesadores simples de bajo costo, por lo general, las operaciones a nivel de bits son sustancialmente más rápidas que la división, varias veces más rápidas que la multiplicación y, a veces, significativamente más rápidas que la suma.
NOTA: Recomendaría el uso de bibliotecas estándar para trabajar con cadenas por varias razones (legibilidad, corrección, portabilidad, etc.). Utilice solo cambio de bits si ha medido el rendimiento y este es su cuello de botella.
Permítanme decir que esto es, aunque parezca inteligente, un truco realmente estúpido.
Si alguien te recomienda esto en 2019, pégale.
Golpéalo tan fuerte como puedas.
Por supuesto, puede hacerlo en su propio software que usted y nadie más utiliza si sabe que nunca usará ningún idioma que no sea el inglés.
De lo contrario, no hay que ir.
El hack fue discutible "OK" hace unos 30-35 años cuando las computadoras realmente no hacían mucho más que el inglés en ASCII, y tal vez uno o dos idiomas europeos importantes. Pero ... ya no es así.
El truco funciona porque las letras mayúsculas y minúsculas de Estados Unidos y América están exactamente
0x20
separadas entre sí y aparecen en el mismo orden, que es solo una pequeña diferencia.
Que, de hecho, este bit hackear, alterna.
Ahora, la gente que creaba páginas de códigos para Europa occidental, y más tarde el consorcio Unicode, era lo suficientemente inteligente como para mantener este esquema, por ejemplo, para los Umlauts alemanes y las voces con acento francés.
No es así para ß que (hasta que alguien convenció al consorcio de Unicode en 2017, y una gran revista impresa de Fake News escribió acerca de eso, en realidad convenció a Duden, sin comentarios al respecto)
ni siquiera existe
como un versal (se transforma en SS) .
Ahora existe como versal, pero las dos son posiciones separadas
0x1DBF
, no
0x20
.
Sin embargo, los implementadores no fueron lo suficientemente considerados como para mantener esto en marcha. Por ejemplo, si aplicas tu pirateo en algunos idiomas de Europa del Este o similares (no sabría sobre cirílico), obtendrás una desagradable sorpresa. Todos esos caracteres "hacha" son ejemplos de eso, minúsculas y mayúsculas son uno aparte. El hack por lo tanto no funciona correctamente allí.
Hay mucho más que considerar, por ejemplo, algunos caracteres no se transforman simplemente de mayúsculas a minúsculas (se reemplazan con secuencias diferentes), o pueden cambiar de forma (lo que requiere diferentes puntos de código).
Ni siquiera pienses en lo que hará este truco con cosas como la tailandesa o la china (solo te dará un completo disparate).
El ahorro de un par de cientos de ciclos de CPU puede haber valido la pena hace 30 años, pero hoy en día, realmente no hay excusa para convertir una cadena correctamente.
Hay funciones de biblioteca para realizar esta tarea no trivial.
El tiempo que se tarda en convertir varias docenas de kilobytes de texto
correctamente
es insignificante en la actualidad.
Xoring con 32 (00100000 en binario) establece o restablece el sexto bit (desde la derecha). Esto es estrictamente equivalente a sumar o restar 32.
Los rangos alfabéticos de mayúsculas y minúsculas no cruzan un límite de "alineación" de
%32
en el sistema de codificación ASCII.
Por esta razón, el bit
0x20
es la única diferencia entre las versiones en mayúsculas / minúsculas de la misma letra.
Si este no fuera el caso, tendría que sumar o restar
0x20
, no solo para alternar, y para algunas letras habría transferencia para voltear otros bits más altos.
(Y no habría una sola operación que pudiera alternar, y la verificación de los caracteres alfabéticos en primer lugar sería más difícil porque no podría | = 0x20 forzar la mayúscula).
Trucos relacionados con ASCII solo:
puede verificar un carácter ASCII alfabético
forzando minúsculas con
c |= 0x20
y luego
verifique
(sin signo)
c - ''a'' <= (''z''-''a'')
.
Así que solo 3 operaciones: OR + SUB + CMP contra una constante 25. Por supuesto, los
compiladores saben cómo optimizar
(c>=''a'' && c<=''z'')
en asm como este para usted
, así que a lo sumo debería haz la parte
c|=0x20
tú mismo.
Es bastante incómodo hacer todo el casting necesario, especialmente para evitar las promociones de enteros predeterminados a
int
firmado.
unsigned char lcase = y|0x20;
if (lcase - ''a'' <= (unsigned)(''z''-''a'')) { // lcase-''a'' will wrap for characters below ''a''
// c is alphabetic ASCII
}
// else it''s not
Vea también Convertir una cadena en C ++ a mayúsculas (SIMD cadena solo para ASCII, enmascarando el operando para XOR usando esa comprobación).
Y también Cómo acceder a una matriz de caracteres y cambiar las letras minúsculas a mayúsculas y viceversa (C con intrínsecos SIMD, y mayúsculas x86 asm mayúsculas y minúsculas para caracteres ASCII alfabéticos, dejando a otros sin modificar).
Estos trucos solo son útiles si se optimiza a mano un poco de procesamiento de texto con SIMD (por ejemplo, SSE2 o NEON), después de verificar que ninguno de los caracteres en un vector tiene establecido su bit alto. (Y, por lo tanto, ninguno de los bytes es parte de una codificación UTF-8 de múltiples bytes para un solo carácter, que podría tener diferentes inversos en mayúsculas / minúsculas). Si encuentra alguno, puede volver a escalar para esta porción de 16 bytes, o para el resto de la cadena.
Incluso hay algunas configuraciones regionales donde
toupper()
o
tolower()
en algunos caracteres en el rango ASCII producen caracteres fuera de ese rango, en particular turco, donde I ↔ ı e İ ↔ i.
En esos lugares, necesitaría una comprobación más sofisticada, o probablemente no intente utilizar esta optimización.
Pero en algunos casos, se le permite asumir ASCII en lugar de UTF-8, por ejemplo, las utilidades de Unix con
LANG=C
(el entorno local de POSIX), no
en_CA.UTF-8
o lo que sea.
Pero si puede verificar que es seguro, puede
toupper
cadenas de longitud media mucho más rápido que llamar a
toupper()
en un bucle (como 5x), y el
último que probé con Boost 1.58
, mucho
más
rápido que
boost::to_upper_copy<char*, std::string>()
que hace un estupido
dynamic_cast
para cada personaje.