maketrans – Tabela de tradução de Strings

Já ouviu falar do alfabeto Leet (ou l337)? É um alfabeto empregado principalmente na internet e usado para comunicação entre pessoas, onde algumas letras do alfabeto latino são substituídas por símbolos graficamente parecidos. Utilizando alfabeto leet, a palavra STREET ficaria 57r337, por exemplo. Ou seja, o número 5 substitui a letra S, o número 7 substitui a letra T, 3 substitui a letra E.

Vamos criar uma ferramenta que traduza texto escrito usando o alfabeto tradicional para texto usando alfabeto leet. Uma forma tosca de fazermos isso é substituir todas as ocorrências de determinada letra do alfabeto tradicional por seu correspondente leet. Exemplo:

s = raw_input('Digite uma frase:')
s = s.replace('a', '4')
s = s.replace('t', '7')
s = s.replace('e', '3')
s = s.replace('s', '5')
...
print s

Porém, existe outra forma mais simples e mais elegante, usando uma Tabela de Tradução. Considere a tabela abaixo. Nela temos duas colunas, uma chamada de Entrada e outra chamada de Saída. Usando a tabela de tradução, cada letra da coluna Entrada encontrada no texto será substituída pela sua correspondente na coluna Saída.

Entrada Saída
A 4
B 8
T 7
E 3
S 5
I 1
O 0
Z 2

No módulo string, fornecido juntamente com a biblioteca padrão do Python, temos um método chamado maketrans, que, dadas duas entradas, cria uma tabela de tradução. Por exemplo, para criar a tabela de tradução apresentada acima, utilizamos o código abaixo:

from string import maketrans
entrada = 'ABTESIOZ'
saida   = '48735102'
tabela = maketrans(entrada, saida)
s = raw_input('Digite uma frase para ser convertida para leet:')
print s.translate(tabela)

As variáveis entrada e saida são strings que irão representar as colunas da tabela de tradução. Para cada ocorrência de um caractere da string entrada em uma string que será traduzida, tal caractere será substituído pelo elemento correspondente na variável saida. Por exemplo, todo caractere ‘A’ em uma string a ser traduzida será substituído pelo caractere ‘4’, e assim por diante.

Usando a mesma idéia, podemos escrever um programinha que cifre uma mensagem usando a Cifra de Caesar. Nesse tipo de cifra, cada letra de uma frase é substituída por outra letra, de acordo com um deslocamento do alfabeto tradicional. Considere como exemplo os dois alfabetos abaixo:

a b c d e f g h i j k l m n o p q r s t u v w x y z
c d e f g h i j k l m n o p q r s t u v w x y z a b

O alfabeto da segunda linha possui um deslocamento de 2 em seus caracteres. Agora, podemos cifrar textos de acordo com tais alfabetos. Por exemplo:

hello world  ---->  jgnnq yqtnf

Cada caractere da string original é substituído pelo correspondente da tabela de tradução. ‘h’ é substituído por ‘j’, e assim por diante. Quem vê a mensagem  jgnnq yqtnf  não consegue descobrir qual o significado desta, a não ser que conheça ou descubra o algoritmo utilizado para cifrá-la. É claro que, nesse caso, é muito simples descobrir. Outro exemplo de cifragem usando os mesmos dois alfabetos:

estou saindo ---->  guvqw uckpfq

Como implementar uma Cifra de Caesar simples, com deslocamento 2, em Python?

from string import maketrans

entrada = 'abcdefghijklmnopqrstuvwxyz'
saida   = 'cdefghijklmnopqrstuvwxyzbc'

def cifra_de_caesar(texto):
    tabela = maketrans(entrada, saida)
    return texto.translate(tabela)

print cifra('hello world')

Como vimos nos exemplos anteriores, o método maketrans, combinado com o translate, nos facilita muito a vida na hora de fazer a cifragem/decifragem de uma mensagem. Poderíamos fazer o mesmo com o método replace da string, mas o código ficaria muito maior e difícil de manter.

Maiores informações sobre maketrans: http://docs.python.org/library/string.html#string-functions

Anúncios

Retorno de múltiplos valores em funções – com tuplas

Quando aprendemos o conceito de funções no estudo de programação, nos é ensinado que uma função é um trecho de código ao qual damos um nome, que realiza uma tarefa específica, e que pode receber várias entradas de uma vez e retornar apenas um valor por vez. Até aí, tudo certo. Mas, quando estamos programando em Python, poderemos nos deparar com códigos semelhantes ao seguinte:

def func(x, y):
    return x*y, x+y

mult, soma = func(2, 3)

Pera aí! Uma função que retorna 2 valores de uma só vez? O valor da multiplicação das entradas será retornado e atribuído à variável mult, e o valor da soma das entradas atribuído à variável soma. Outro exemplo:

def func(x, y):
    return x+y, x-y, x*y, x/y, x%y, "hello"

Uma função com seis valores de retorno ao mesmo tempo? Quem, num momento de desespero e extrema gambiarra, nunca pensou em definir uma struct só para agrupar dois ou três valores que desejava que fossem retornados todos de uma vez por uma única função? Pois é, em Python é possível retornar mais de um valor em uma função. Porém, nem tudo é mágica. Para entender, vamos começar por um exemplo. Abra um shell Python e crie uma variável da seguinte forma:

>>> x = 2, 4, 10

Você deve estar pensando: “Isso não vai funcionar! Como posso eu atribuir 3 valores para apenas uma variável?”. O que está esperando para testar? Viu? Funcionou. Mas como? Calma, o próximo passo é vermos o conteúdo de x após a atribuição:

>>> print x
(2, 4, 10)

Agora, vamos verificar qual é o tipo de dados da variável x:

>>> type(x)
<type 'tuple'>

Opa! Então, quer dizer que quando eu faço return x*y, x+y, estou na verdade retornando uma TUPLA? Isso mesmo. Mais legal que isso somente o fato de Python suportar a seguinte forma de atribuição de elementos de tuplas:

x = 1 , 2, 10
a, b, c = x

Após as atribuições da segunda linha, o valor de a será 1, de b será 2 e de c será 10. Legal né? É exatamente isso que acontece quando criamos uma função com “mais de um valor de retorno”. Na realidade, nossa função possui apenas um valor de retorno, que é uma tupla. E os elementos dessa tupla são atribuídos à variáveis individuais no chamador da função.

Mais sobre tuplas: http://www.franciscosouza.com.br/aprendacompy/capitulo_09.html

Leitura de arquivos de configuração .ini em Python

Embora não seja a melhor forma possível de descrevermos a configuração de algo, os arquivos .INI ainda existem em grande quantidade. Quem nunca viu ou precisou que seu programa lesse um arquivo parecido com o seguinte?

[section1]
config1=100
config2=0
[section2]
confign=-1

Por mais que queiramos evitar tais arquivos, por serem até considerados um formado obsoleto, às vezes é necessário ler um arquivo desses para obter informações para nosso programa. Como fazer isso em Python? Ler o arquivo linha por linha? Não! Vamos usar o módulo ConfigParser.

Primeiramente, devemos importar o módulo, instanciar um objeto ConfigParser e realizar a leitura do arquivo de configuração desejado (no nosso caso, config.ini):

import ConfigParser
cfg = ConfigParser.ConfigParser()
cfg.read('config.ini')

Feito isso, o arquivo de configuração já está lido, agora basta que obtenhamos os valores que queremos extrair do arquivo. Para obter um valor do arquivo, é preciso especificar a seção e a propriedade que queremos obter. Por exemplo, o código abaixo obtém o valor da propriedade confign da seção section2 e o armazena na variável x:

x = cfg.getint('section2', 'confign')

Veja que utilizamos um método chamado getint( ) para fazer a leitura de um valor inteiro do arquivo .INI. Caso os dados a serem lidos fossem de outro tipo, poderíamos usar um dos seguintes métodos: getboolean( ), getfloat( ), ou simplesmente get( ) no caso de strings.

Com o mesmo módulo, também é possível realizarmos a escrita de arquivos .ini. Veja mais em: http://docs.python.org/library/configparser.html

Tratando argumentos com argparse

No post anterior, vimos o que são e como tratar manualmente os argumentos vindos para o programa pela linha de comando do usuário. Como vimos, é trabalhoso e chato tratar todas as possíveis opções de nosso programa. Para facilitar nossa vida, existem várias bibliotecas para tratamento dessas opções. Em Python, temos algumas opções, e no post de hoje iremos ver um pouco do módulo argparse. Basta informarmos a ele quais são os argumentos esperados para nosso programa, que o trabalho de realizar a verificação e atribuição de entradas à variáveis internas é feito pelo código do argparse. Além disso, o argparse gera automaticamente o texto de ajuda e o apresenta quando o usuário passa argumentos incorretos.

Para exemplificar, vamos escrever um programa simples que recebe como argumentos dois valores, identificados por opções, da seguinte forma:

$ prog.py --frase "ola mundo" -n 10

O programa então imprime na tela a frase recebida como entrada n vezes.

Em primero lugar, importamos o módulo argparse e instanciamos um objeto ArgumentParser, que será o responsável por fazer a análise dos argumentos fornecidos pela linha de comando.

1: import argparse
2:  parser = argparse.ArgumentParser(description = 'Um programa de exemplo.')

Após isso, devemos configurar nosso parser, informando a ele quais são os argumentos esperados pelo nosso programa.

3:  parser.add_argument('--frase', action = 'store', dest = 'frase',
                           default = 'Hello, world!', required = False,
                           help = 'A frase que deseja imprimir n vezes.')
4:  parser.add_argument('-n', action = 'store', dest = 'n', required = True,
                           help = 'O número de vezes que a frase será impressa.')

A linha 3 cria o argumento –frase (cujo valor será passado como argumento seguinte), sendo que a ação a ser tomada pelo argparse ao encontrar tal argumento será armazenar (action = ‘store’) o valor fornecido em uma variável interna do programa referenciada pelo nome frase (dest = ‘frase’). Tal argumento é opcional (required = False) e, caso o usuário não forneça ele, o valor padrão será ‘Hello, world!’. Caso o usuário solicite ajuda do programa (através do atributo padrão -h), será mostrado um texto que inclui a informação sobre o argumento –frase. O mesmo foi feito para o argumento -n, com a diferença de que este é um argumento obrigatório.

Após configurá-lo, devemos solicitar ao nosso parser para que faça a verificação dos argumentos.

5:  arguments = parser.parse_args()

A linha 5 mostra a chamada ao parser para que este faça a verificação dos argumentos. Como resultado, ele nos devolve um objeto (do tipo Namespace), que irá conter variáveis internas com os valores fornecidos pelo usuário pela linha de comando para os argumentos. Vejamos a sequência do programa:

6: for i in range(0, int(arguments.n)):
7:      print arguments.frase

Como podemos ver, os valores passados pelo usuário para frase e para n são acessados como atributos do objeto arguments. Pronto, temos um programinha bem simples que recebe argumentos pela linha de comando sem a necessidade de realizar o tratamento manual dos argumentos. Vamos ver a execução desse programa, que chamei de exemplo.py:

$ exemplo.py
usage: exemplo.py [-h] [--frase FRASE] -n N
exemplo.py: error: argument -n is required
$ exemplo.py --frase "olá"
usage: exemplo.py [-h] [--frase FRASE] -n N
exemplo.py: error: argument -n is required
$ exemplo.py --frase "olá" -n 3
olá
olá
olá

E, para finalizar, vamos ver o comportamento do programa quando o usuário fornece o argumento -h. Por padrão, o argparse monta um texto de ajuda, com base nas informações fornecidas na construção do parser.

 
$ exemplo.py -h
usage: exemplo.py [-h] [--frase FRASE] -n N

Um programa de exemplo.

optional arguments:
  -h, --help     show this help message and exit
  --frase FRASE  A frase que deseja imprimir n vezes.
  -n N           O número de vezes que a frase será impressa.

Além do argparse, o getopts também é um módulo muito utilizado para tratamento de opções de linha de comando. É isso, com módulos como esse, nossa vida fica muito mais fácil. 🙂

Argumentos da linha de comando

Como é comum em programas de linha de comando em sistemas baseados em UNIX, é muito útil que nossos programas possuam uma “interface” de linha de comando, de forma que o usuário possa fornecer entradas ao programa diretamente pela linha de comando. Tais entradas são comumente chamadas de argumentos e são muito mais práticas para o usuário do que encher o programa de input() e raw_input(). Vejamos um exemplo de passagem de argumentos em um famoso programa para Linux:

$ ifconfig eth0 address 192.168.0.10

No exemplo acima, ifconfig é o nome do programa, e o restante (eth0 address 192.168.0.10) são os argumentos para esse programa. Por que isso é mais prático do que o seguinte?

$ ifconfig
Digite a interface de rede que deseja configurar: eth0
Digite o endereço para a interface de rede: 192.168.0.10
$

Simples. O primeiro exemplo permite que seja criado um script com entradas dinâmicas, baseadas no conteúdo de variáveis, permite fácil repetição da execução dos comandos (juntamente com as entradas) através das funções de histórico do shell, sem falar que possibilita comunicação entre programas de forma facilitada. OK, mas como fazemos para obter as entradas do usuário vindas da linha de comando em Python?

Existe mais de uma resposta para essa pergunta. Vamos à mais comum:

import sys
print sys.argv[0]
print sys.argv[1]
print sys.argv[2]

O exemplo acima mostra o uso do atributo argv do módulo sys. Através desse atributo, podemos acessar as entradas passadas pelo usuário. Considere que o código acima é o conteúdo de um arquivo chamado args.py. Se esse arquivo for executado da seguinte forma:

$ python args.py Hello world

Produzirá a seguinte saída:

args.py
Hello
world

Isso mesmo, como podemos ver, o primeiro argumento para o programa é o próprio nome do programa, e é acessado através de sys.argv[0]. A variável argv nada mais é do que uma lista. Sendo assim, seus elementos podem ser acessados via índice. E o que acontece se chamarmos o programa acima sem argumentos?

$ python args.py
args.py
Traceback (most recent call last):
  File "args.py", line 5, in <module>
    print sys.argv[1]
IndexError: list index out of range

Temos um erro de execução, pois nosso código tenta acessar elementos inexistentes na lista argv. O que fazer? Antes de acessar um elemento, devemos checar se a lista o contém.

import sys
if len(sys.argv) >= 3:
    print sys.argv[0]
 print sys.argv[1]
 print sys.argv[2]

Mas e aqueles programas que possuem diversas opções pela linha de comando, sendo algumas obrigatórias, outras opcionais, como fazem? Tratam opção por opção?

Até podem fazer isso de forma manual, mas existem ferramentas para manipulação de argumentos vindos da linha de comando que facilitam bastante o trabalho. Uma delas será o tema do próximo post: a argparse.

Até breve.

Fatiamento (slicing) de Strings em Python

Antes de falarmos de slicing, vamos ver rapidamente o que são as strings em Python. Strings em Python são objetos como outros quaisquer. Podem ser construídos com uma atribuição simples:

>>> s = "hello, world!"

Tendo feito isso, o objeto s possui disponíveis vários métodos:

>>> print s.upper()
HELLO, WORLD!
>>> print s.split(",")
['hello', ' world!']
>>> print s.split(",")[0]
'hello'
>>> print s.replace("world", "dude")
hello, dude!

Acima, são mostrados apenas alguns dos métodos disponíveis para as strings, nativamente em Python. Assim como em outras linguagens, elementos individuais de uma string podem ser acessados via índice:

>>> print s[0]
h
>>> print s[2]
l
>>> print s[12]
!

Para acessar o último elemento de uma string, podemos proceder de duas formas:

>>> print s[len(s)]
!
>>> print s[-1]
!

Uma operação muito interessante que Python fornece para manipulação de strings é o fatiamento (slicing). Fatiamento significa extrair apenas uma parte da string, ou seja, uma substring. Com essa operação, podemos delimitar os limites inferior e superior do pedaço da string que queremos acessar. Por exemplo, se quisermos acessar a substring da posição 0 até a posição 4 na string s original, podemos fazer o seguinte:

>>> print s[0:5]
'hello'
>>> print s[:5]
'hello'
>>> print s[2:4]
ll
>>> print s[7:13]
'world!'
>>> print s[7:]
'world!'
>>> print s[:]
'hello, world!'

Repare que o elemento que reside na posição do limite superior não é retornado juntamente com a string. Veja a primeira linha do código acima, s[0:5] retorna os elementos que residem entre as posições 0 e 5, incluindo a primeira e excluindo a segunda. Como mostrado acima, também podemos omitir um dos limites, ou ambos, quando queremos algo do início até posição x, ou da posição x até o fim.

OK, mas pra que serve isso? Vamos a um exemplo bem simples: extrair o protocolo de uma URL (formato: protocolo://servidor:porta/caminho/para/recurso).

url = "http://localhost:8000/arquivo.iso"
protocolo = url[0:url.index(':')]

Esse é apenas um simples exemplo do poder que essa operação tem. Cabe ressaltar aqui, que o fatiamento cria uma nova string contendo o conteúdo solicitado na operação. Ou seja, a cada operação de fatiamento, uma nova string é criada.

É isso. Faça bom proveito desse recurso que torna o código muito mais limpo e de fácil entendimento.

Obtendo a data e a hora atuais em Python

Para obtermos os valores de data e hora atuais, podemos utilizar o módulo datetime, que fornece formas bem simples para fazermos isso. Basta utilizar o método now() existente na classe:

from datetime import datetime
now = datetime.now()
print now.year
print now.month
print now.day
print now.hour
print now.minute
print now.second


Corrigido, de acordo com o comentário do eljunior.