sexta-feira, 27 de setembro de 2013

Introdução ao código ASCII (para iniciantes)

Atualizado no blog novo.

Glider Código ASCII é a codificação de caracteres para valores numéricos de 7 bits mais aceita mundialmente e significa American Standard Code for Information Interchange – portanto, nada de ASC-2, por favor.

O ASCII não permite acentos ou diacríticos, apenas os caracteres mais comuns e uma série de 33 caracteres de controle. Porém, como a menor palavra binária (sequência de bits) usada é o byte, que usa 8 bits, um a mais que o ASCII, é possível criar supersets com o dobro1 da capacidade como, por exemplo, o Latin-1 e o CP-1252.

Para quem está começando a tentar entender o código ASCII, é preciso saber que ele é composto de grupos. O primeiro grupo é o que possui sequência binária 00xxxxx, ou seja, dois zeros nas primeiras casas e qualquer combinação nas 5 seguintes. Isso dá uma combinação de 32 caracteres, com código de 0 (0000000) a 31 (0011111), e consiste nos caracteres de controle, como nulo (0), os caracteres de mudança de linha (10 e 13), o backspace (8), o escape (27) e o tab (9).

Há uma exceção, um carácter de controle perdido fora dessa sequência: o delete (127 – 1111111).

O grupo seguinte, 010xxxx (32 a 47) é uma sequência de caracteres de pontuação e símbolos matemáticos, a começar pelo espaço (32).

Então começam os números, dentro do grupo 011xxxx. Os números começam em 0110000 (48) e seguem até 0111001 (57). Uma forma fácil de pensar qual o código de um dígito numérico é somar 48 ao valor do número. Por exemplo, o código de 7 é (48+7=) 55.

O grupo 011xxxx segue com mais pontuações e símbolos matemáticos.

O o grupo seguinte é o das letras maiúsculas: 10xxxxx. O primeiro elemento, 64 (1000000), é o arroba (@), seguido das letras. Para saber o código de uma letra é só somar 64 ao índice da letra no alfabeto. Por exemplo, a letra F é 6ª letra do alfabeto, portanto seu código é (64+6=) 70.

Depois do Z (90), continuam mais pontuações e símbolos matemáticos, terminando com underscore (, 95 – 1011111).

A próxima sequência, começando pela crase, são as letras minúsculas. A sequência é idêntica à anterior, apenas trocando o 6º bit (da direita pra esquerda) por 1: 11xxxxx, o que soma 32 ao número, e as letras coincidem o mesmo código.

Ou seja, se o código de F é 70, o código de f é (70+32=) 102.

Como esperado, a sequência após o z (122) continua com pontuações e símbolos matemáticos.

Exemplo

Como exemplo, vamos codificar a palavra Kodumaro:
K = 64 + 11      =  75
o = 64 + 15 + 32 = 111
d = 64 +  4 + 32 = 100
u = 64 + 21 + 32 = 117
m = 64 + 13 + 32 = 109
a = 64 +  1 + 32 =  97
r = 64 + 18 + 32 = 114
o = 64 + 15 + 32 = 111

Kodumaro ≡ 75, 111, 100, 117, 109, 97, 114, 111

Se você preferir, pode pensar em bits (é incrível, mas é mais simples): o 7º bit (1º da esquerda) é sempre 1 (letra), o 6º (2º da esquerda) é 0 para maiúsculas e 1 para minúsculas, os 5 seguintes são a ordem da letra no alfabeto:
A =  1 = 00001
B =  2 = 00010
C =  3 = 00011
D =  4 = 00100
E =  5 = 00101
F =  6 = 00110
G =  7 = 00111
H =  8 = 01000
I =  9 = 01001
J = 10 = 01010
K = 11 = 01011
L = 12 = 01100
M = 13 = 01101
N = 14 = 01110
O = 15 = 01111
P = 16 = 10000
Q = 17 = 10001
R = 18 = 10010
S = 19 = 10011
T = 20 = 10100
U = 21 = 10101
V = 22 = 10110
W = 23 = 10111
X = 24 = 11000
Y = 25 = 11001
Z = 26 = 11010

Voltando ao Kodumaro:
K = (letra)(maiúscula)11ª – 1.0.01011 – 1001011
o = (letra)(minúscula)15ª – 1.1.01111 – 1101111
d = (letra)(minúscula) 4ª – 1.1.00100 – 1100100
u = (letra)(minúscula)21ª – 1.1.10101 – 1110101
m = (letra)(minúscula)13ª – 1.1.01101 – 1101101
a = (letra)(minúscula) 1ª – 1.1.00001 – 1100001
r = (letra)(minúscula)18ª – 1.1.10010 – 1110010
o = (letra)(minúscula)15ª – 1.1.01111 – 1101111

E de fato é assim que é codificado e armazenado:
1001011.1101111.1100100.1110101.1101101.1100001.1110010.1101111

Ou melhor, em bytes:
0100101101101111011001000111010101101101011000010111001001101111


[]’s
Cacilhας, La Batalema


1A cada bit que se acrescenta a uma palavra binária, sua quantidade de combinações dobra.

quarta-feira, 18 de setembro de 2013

Um editor de textos rapidinho

Veremos como é simples criar uma aplicação gráfica rapidamente usando Tkinter.

Vamos começar a a partir de um boilerplate:

#!/usr/bin/env python
# coding: UTF-8
from __future__ import absolute_import, division, print_function, unicode_literals

#-------------------------------------------------------------
# Cabeçalhos
#

# Aqui vão entrar os imports

__all__ = ['Editor']


#-------------------------------------------------------------

class Editor(object):

    def mainloop(self):
        pass


#-------------------------------------------------------------
# Rodapé

if __name__ == '__main__':
    app = Editor()
    app.mainloop()

A primeira coisa que precisamos fazer é criar nossa janela raiz. Para tanto, adicione aos cabeçalhos o import:

from Tkinter import *

Método construtor

Em seguida, no começo da classe Editor, vamos começar a escrever nosso construtor:

class Editor(object):

    def __init__(self):
        root = self.root = Tk()
        root.title('EditPy')

Precisamos então de um frame para colocar os botões de abrir, salvar e salvar-como:

        fbuttons = Frame(root)
        Button(fbuttons, text='Open', command=self.open) \
              .pack(side=LEFT)
        Button(fbuttons, text='Save', command=self.save) \
              .pack(side=LEFT)
        Button(fbuttons, text='Save As', command=self.save_as) \
              .pack(side=LEFT)

O pai (master) do frame fbuttons é nossa janela raiz e o pai dos botões é o frame.

Agora precisamos de uma caixa de texto para editar e exibir o conteúdo dos arquivos. Primeiro vá aos cabeçalhos novamente e acrescente o import:

from ScrolledText import ScrolledText

Então, continuando o construtor, crie a caixa de texto com rolagem:

        steditor = self.steditor = ScrolledText(root)

Para fechar o construtor só falta exibir o frame e a caixa de texto, e colocar o foco na caixa:

        fbuttons.pack(expand=True, fill=X)
        steditor.pack(expand=True, fill=BOTH)
        steditor.focus()

Loop principal

Se você tentar rodar o código agora, nada acontece. Isso porque o método mainloop() faz isso: absolutamente nada.

Precisamos então, neste método, chamar o loop principal do Tkinter. Para isso, altere o método:

    def mainloop(self):
        self.root.mainloop()

Chamamos então o loop principal através da janela raiz (root).

Abrir um arquivo

Porém coisas estranhas e erros esdrúxulos podem acontecer, pois ainda não implementamos nenhum dos métodos chamados pelos botões – self.open, self.save e self.save_as.

Parece-me justo que comecemos pelo método de abertura de arquivo. Criemos a assinatura do método:

    def open(self):

Para abrir um arquivo, precisamos de uma ferramenta chamada askopenfilename que precisamos importar, então nos cabeçalhos acrescente:

from tkFileDialog import askopenfilename

Então, de volta ao método open(), podemos pegar o nome do arquivo e carregá-lo:

        filename = askopenfilename(title='EditPy | Open')
        if filename:
            self.filename = filename
            self.load_file()

O método load_file() é quem vai de fato carregar o arquivo, mas precisamos antes apagar o conteúdo da caixa de texto:

    def load_file(self):
        filename = self.filename
        steditor = self.steditor

        steditor.delete('@0,0', 'end')
        with open(filename) as fd:
            steditor.insert('@0,0', fd.read())

O método delete() da caixa de texto apaga parte do conteúdo da caixa e os parâmetros são os índices de começo e fim:
  • @0,0 significa linha 0, coluna 0 (começo do texto);
  • end significa o final do texto.

O método insert() inserte a string (lida do descritor de arquivo) na posição indicada (@0,0).

Salvando o texto

Para salvar o texto, precisamos implementar o método save(). Ele precisa:
  1. Verificar se temos um nome de arquivo – se não tivermos, é um save_as.
  2. Abrir o arquivo para escrita.
  3. Obter o texto da caixa de texto.
  4. Gravar o texto no arquivo.

Simples, indolor e o código é intuitivo:

    def save(self):
        filename = self.filename
        if not filename:
            return self.save_as()

        with open(filename, 'w') as fd:
            fd.write(
                self.steditor.get('@0,0', 'end')
            )

Repare nos índices usados no método get().

Em seguida é preciso saber o que fazer quando não temos o nome do arquivo ou quando escolhemos Save As.

Primeiro a assinatura do método:

    def save_as(self):

Caso haja um nome de arquivo, é honesto usá-lo como ponto de partida para caixa de diálogo de salvamento. Para isso precisamos separar o nome de diretório do nome base do arquivo e uma forma de fazer isso é com o método de string rfind.

Para sabermos qual o separador de diretórios usado, adicione aos imports:

from os import path

Então continue no método:

        filename, directory = self.filename, None
        if filename and path.sep in filename:
            index = filename.rfind(path.sep) + 1
            directory = filename[:index]
            filename = filename[index:]

[update 2014-04-12]
Optei por usar rfind, mas poderia ter usado path.basename e path.dirname. É uma questão de escolha e estilo.
[/update]
Feito o slice, já podemos exibir a caixa de diálogo de salvamento. Para isso precisamos da ferramenta asksaveasfilename.

Modifique o import do askopenfilename para:

from tkFileDialog import askopenfilename, asksaveasfilename

Continuando agora no método save_as(), podemos exibir a caixa de diálogo e, se algum arquivo for informado, chamar o método save():

        filename = asksaveasfilename(
            title = 'EditPy | Save As',
            initialfile = filename,
            initialdir = directory,
        )

        if filename:
            self.filename = filename
            self.save()

E pronto! Já pode testar seu editor de texto!

Lapidando

Uma forma de melhorar um pouco o visual da aplicação é usando a extensão ajulejada do tk, chamada ttk.

Para tanto, basta adicionar ao cabeçalho, no final dos imports:

from ttk import *

Você também pode usar os métodos wm_protocol() e bind_all() da janela raiz para adicionar funcionalidades interessantes, mas isso é história pra outro dia. ;-)

[]’s
Cacilhας, La Batalema