|
Esmiuçando operadores bit-a-bit - Parte 2
por Alisson Carlos de F. Rodrigues
Na segunda parte de artigos sobre operadores bit-a-bit, irei abordar um outro lado desses operadores: caracteres. No primeiro artigo o foco foi “números”, apresentando a maneira como os operadores bit-a-bit lidam com eles. No caso de caracteres eles funcionam de uma maneira um pouco diferente, e é exatamente dessas diferenças que trato nesse novo artigo.
|
Continuação do artigo: http://www.phpbrasil.com/articles/article.php/id/1392
Como dito no artigo anterior, os operadores bit-a-bit tratam diretamente bits, que são os 0s e 1s lidos pelo processador.
Acredito que qualquer um que esteja lendo esse artigo, e que tenha uma noção básica teórica sobre como funcionam as coisas dentro de um computador, sabe que normalmente (não mais definitivamente) é usado caracteres seguindo a tabela ASCII (Unicode é história pra outro artigo), que são exatamente 255 caracteres que servem pra representar a escrita padrão Ocidental com mais alguns caracteres especiais e ponto.
A cada um desses caracteres é designado ou atribuído um número que vai de 0 (NULL) à 255, 11111111 em binário ou FF em hexadecimal (vazio). Se você leu o artigo anterior (e eu recomendo pra entender este), então deve lembrar que em cores RGB também é usado algo parecido.
http://img329.imageshack.us/img329/6633/asciipq9.gif
Aí em cima está a tabela ASCII com todos os seus 255 caracteres.
Indo direto aos exemplos, o problema: Vamos criar uma função que faça a mesma coisa que funções como strtolower, strtoupper, ucwords, etc. Pra isso também iremos usar um pouco de expressões regulares (excelente tutorial/livro em guia-er.sourceforge.net) e obviamente, operadores bit-a-bit.
Primeiro temos que analisar a relação entre letras maiúsculas e letras minúsculas dentro da tabela ASCII. Tomemos como exemplo a letra “A”. Como maiúscula ela tem o código 65 e como minúscula 97. Diferença de 32 (97-65) que equivale ao caractere espaço (procure sempre olhar esses dados na tabela). Vamos visualizar esses números como binários:
65: 1000001
97: 1100001
32: 0100000
|
Agora você precisa lembrar como funcionam os operadores “&”, “^” e “|”. Supondo que você queira passar uma letra maiúscula para minúscula você precisará usar o operador “|”. Lembre-se que com esse operador, na posição que tiver pelo menos um número “1” será colocado obviamente um número “1” e onde tiver dois 0s um número “0”. Neste caso você tem a letra “A” (ASCII 65) e vai passar para maiúscula, portanto você deve comparar com “ “ (ASCII 32), analise o esquema abaixo seguindo a regra do operador “|”, assim você chega ao valor 97:
65: 1100001
32: 0100000
97: 1100001
|
Agora supondo que você tenha a letra “a” (ASCII 97) e queira passar para maiúscula, nesse caso você precisa usar o operador “^”. Lembre-se, com esse operador é atribuído um número “1” onde tiver um “1” e um “0”. Seria basicamente o mesmo do OR, mas se tiver dois números “1” na posição será então atribuído o valor “0”. Observe agora os valores abaixo e analise seguindo a regra do operador “^”, você vai chegar ao valor 65:
97: 1100001
32: 0100000
65: 1000001
|
Na próxima página eu mostro como isso pode ser feito em PHP e passo a função final de strtolower e strtoupper.
strtolower
Neste caso o importante são as letras em maiúsculo, que serão passadas para minúsculo (isso é óbvio, você não vai passar uma letra minúscula para minúscula). Lembre-se dos exemplos da página anterior, no primeiro caso você tem “A” e quer obter “a”, pra isso você usa o operador “|”, em PHP ficaria assim:
<?php
$novaLetra = ‘A’ | ‘ ‘;
?>
|
$novaLetra vai conter então o valor “a”.
strtoupper
É o mesmo caso no primeiro só que ao contrário, você tem a letra minúscula e quer passar para maiúsculo. No exemplo anterior você tinha “a” e queria obter “A”, para isso usou o operador “^”, em PHP ficaria assim:
<?php
$novaLetra = ‘a’ ^ ‘ ‘;
?>
|
Funções completas
As duas funções são bastante simples e semelhantes. Você precisa usar um pouco de ER, é uma ER simples, não precisa de muito “knowledge” pra entender. No caso do strtolower a ER vai pegar todas as letras maiúsculas (e somente elas) e jogar dentro do operador bit-a-bit. Usamos o modificador “e” em preg_replace (que, aliás, só é usado nessa função). Este modificador diz à função que deve ser executado código dentro do segundo parâmetro. No nosso caso, o código seria o operador bit-a-bit. Mesmo sendo código, este deve ser colocado dentro de aspas (esta é uma regra do operador “e”, você encontra explicação pra isso no site no PHP na parte de ERs no padrão perl). O código ficaria assim:
<?php
function strtolower2($texto)
{
$novoTexto = preg_replace("#([A-ZÁÂÀÃÄÉÊËÈÍÎÏÌÓÔÒÕÖÚÛÙÜ])#e","'\\1' | ' '",$text);
return $novaTexto;
}
?>
|
A função preg_replace vai substituir toda ocorrência de letra maiúscula por sua correspondente minúscula jogando-a no segundo parâmetro com o operador bit-a-bit “|”.
Pra função strtoupper é exatamente a mesma coisa, obviamente você deve mudar de maiúsculo para minúsculo e mudar o operador pra “^”:
<?php
function strtoupper2($text)
{
$novoTexto = preg_replace("#([a-záâàãäéêëèíîïìóôòõöúûùü])#e","'\\1' ^ ' '",$text);
return $novoTexto;
}
?>
|
Obviamente as funções originais apresentam um melhor desempenho já que são nativas e, portanto, são criadas diretamente na linguagem mãe (no caso do PHP é C). Uma função utilizando exatamente a mesma lógica nas duas linguagens teriam desempenhos diferentes, sendo mais rápido em C. Mesmo assim, fiz alguns testes aqui e cheguei à conclusão de que essa função leva em torno do dobro do tempo da função nativa. É um excelente resultado.
Na última página ainda temos alguns casos estranhos na forma como o PHP lida com operadores bit-a-bit (ou com a tabela ASCII).
Esta é sem dúvida uma coisa que deve ser levada em consideração na hora de trabalhar com operadores bit-a-bit em caracteres. Na verdade nem é uma coisa ruim, muito pelo contrário, o PHP parece entender o que eu quero que ele faça e age da forma que eu gostaria que ele funcionasse.
Se você reparar na tabela ASCII postada na primeira página, vai reparar que as letras equivalentes entre maiúsculas e minúsculas só têm a relação (diferença de 32) entre os caracteres normais (sem acentos). Parece que os outros caracteres foram simplesmente jogados ali de forma aleatória.
Pra vocês conseguirem visualizar melhor, pegue qualquer letra com acento, “á” (ASCII 160) por exemplo e siga a lógica utilizada lá na primeira página. Converta para binário esse 160 (10100000) e então compare com o espaço utilizando o esquema do operador “^” (que eu usei pra passar de minúscula para maiúscula):
160: 10100000
032: 00100000
128: 10000000
|
Como você pode reparar ali, chegamos ao valor 128, que não tem nada a ver com o valor ASCII de “Á” (a letra maiúscula relativa de “á”), mas sim, é um “Ç”. Agora faça um teste usando o PHP, pegue esse caractere “á” e compare com o espaço usando o operador “^”.
<?php
$letra = 'á' ^ ' ';
print $letra;
?>
|
O que aconteceu? Apareceu “Á” e não “Ç”. Não está entendendo mais nada? Pois agora você vai entender menos ainda. Vamos novamente ao PHP, atribua o valor, usando o operador, à uma variável assim como eu fiz ali em cima, e use a função do PHP que imprime o valor ASCII: ord():
<?php
$letra = 'á' ^ ' ';
print ord($letra);
?>
|
Imprimiu 193, você pode fazer o teste, segure a tecla alt, digite 193 e então solte alt, vai aparecer “┴”, que... adivinha... não é um “Á”, isso é óbvio. Com tudo isso, só me resta acreditar numa coisa, os desenvolvedores do PHP, simplesmente resolveram criar sua própria tabela ASCII e usá-la dentro do PHP. E o que mais me deixa de cara é no próprio manual falar que a função ord() retorna o valor ASCII, passa o link do site asciitable.com que diz que 193 é “┴” e não “Á” e o PHP simplesmente diz o contrário. Pra desencardo de consciência, vá novamente ao PHP e digite chr(193). O que imprime? “Á”.
Aí você me pergunta: mas então os operadores bit-a-bit do PHP não funcionam corretamente? Funciona, a tabela ASCII é que parece que não, se você usar chr(“á”), vai imprimir 225, daí faria sentido, pois 225-193=32. O que não faz sentido é que isso não é ASCII.
Eu até acho que a tal tabela ASCII do PHP faz mais sentido, senão a minha função não funcionaria. Mas então no site poderia dizer que é tabela “outro nome aqui”, como tabela chuchuca, tabela chinchila, sei lá, mas não tabela ASCII.
Se eu errei alguma coisa na minha lógica e por isso deu esse rolo todo aí e alguém me alertar sobre, eu até crio uma “Parte 3” aqui pra tentar explicar esse rolo com a tabela ASCII. Mas se for algo como “Os caras do PHP acharam que ficaria mais fácil” então não tem explicação não, eu até agradeço aos desenvolvedores do PHP, pois fica mais fácil lidar com caracteres. Mas se você quiser usar esses mesmos valores (números) em outros lugares, simplesmente não vai funcionar.
Chegamos ao final da “Parte 2” de artigos sobre Operadores bit-a-bit. Acredito realmente que já abordei tudo o que operadores bit-a-bit podem fazer. Sendo assim, acredito que não precise de uma terceira parte, basta vocês usarem vossa criatividade. Qualquer coisa me mandem um e-mail, se precisar realmente explicar alguma outra coisa, pode deixar que eu crio sim uma parte 3.
Isso aí galera, até a próxima.
|
|
|