Lendo e gravando dados com Plua em arquivos pdb (palm data base)

A alguns dias houve um tópico interessante na lista de discussão sobre plua no yahoo groups (a lista é fechada, por isso passei um link alternativo).

O sujeito Jimmy Joe Jack, disse que queria fazer algo para seu palm com plua, mas não tinha muito tempo para aprender e por isso não queria ler a documentação toda (que é pequena, mas longe de ser fácil), pediu que enviássemos alguns exemplos.

Foi quando Berkant Atay respondeu, mandando um exemplo que fazia algo quando apertava um botão, e na sequecia mandou exemplos de como trabalhar com arquivos, stream e data bases.

Aproveitei a deixa para também aprender como isso afinal funcionava (obrigado Berkant). Deixo agora neste post o que aprendi com meu primeiro exemplo gravando informações em arquivos.

Fazendo a leitura de um arquivo de banco de dados

Plua disponibiliza uma série de funções, veja na documentação lá onde diz "Database I/O functions". Vamos a um trecho de códigos que fica mais fácil.
 -- open in read mode
file,NumRegs = io.open("db:/AgesNames", "r+")

-- define a variable
returnData = {}

-- Atention!in DB file, the index begin in 0 (zero)
-- but in table, array..the index begin in 1 (one)
for i=0,NumRegs-1 do
-- open data from index
file:openrec(i) -- begin in zero
-- read this data
data = file:read("*a")
temp = bin.unpack("SB",data)

-- add this data to returning variable
returnData[i+1] = temp[2].."\t"..temp[1]
end

file:close()

Logo no início eu abro o arquivo com "r+" através do "protocolo" "db", disse que o nome de meu banco de dados é "AgesNames". Assim como em outras linguagens há modos de abrir aquivos. vejamos (tradução livre do manual):
Você deve ter notado que duas variáveis (file e NumRegs) é que recebem o io.open, isso porque Lua suporta atribuição (e retorno) múltipla. No caso io.open retorna o ponteiro para o arquivo (guardei em file) e número de registros/linhas naquele arquivo (guardei em NumRegs).

Em seguida fazemos um laço da primeira a última linha de dados do arquivo. Atenção que o primeiro índice de um arquivo é 0 (zero), enquanto que as tables (arrays, vectors) em Lua iniciam no índice 1.

Você deve ter notado que no "read" também tem uma letra, pois bem, veja o que o manual da Lua diz:
E o último e talvez mais importante detalhe na leitura de alguma informação. Como funciona esse "bin.unpack". Pois bem, o arquivo de dados é um arquivo binário, portanto ocupa menos espaço que um arquivo texto, como isso ocorre? Da mesma forma que em banco de dados, os dados são armazenados no seu tipo e não tudo como string (que ocupa mais bites). Então tanto na hora de ler como gravar devemos informar que tipo de dados é cada coluna.

No caso deste programa temos duas colunas "name" e "age", o primeiro uma string e o segundo um número inteiro positivo. Por causa dessas duas colunas é que temos duas letras dentro do bin.unpack, uma letra dizendo que tipo de dados é cada coluna. Veja no manual da Plua o que diz de cada um dos tipos (tradução livre).

Bom, chegamos em um nível em que conforme sua estrutura de dados, você pode prever quanto espaço na memória um registro vai ocupar, e fazer projeções de quantos registros a memória do seu Palm vai suportar.

A última parte do código, eu concateno as duas colunas de dados em uma, e armazeno então uma matriz em um vector, que depois vai ser usado como fonte de dados para uma gui.list.

Gravando em um arquivo

Agora que você já sabe o que cada coisa significa, a gravação é fácil. Primeiro eu pego os dados dos meus campos da interface (gui.field), coloco isso em uma table (vector), dou um "bin.pack", aloco o espaço necessário para aqueles dados e mando gravar, e claro, fecho o arquivo.

 -- retrive values from fields
name = gui.gettext(dataName)
age = gui.gettext(dataAge)
dataToWrite = {name,age}

-- open for write the data base file
file = io.open("db:/AgesNames","w")

-- encode, acording the data tyes
packed = bin.pack("SB",dataToWrite)
-- how many size is this data?
index = file:createrec(string.len(packed))
-- alloc this size on file
file:openrec(index)
-- finaly write
file:write(packed)
-- and close file
file:close()

O desenho da interface, e como saber que o botão "add" foi pressionado

Tenha bons costumes, sempre que inicia a desenhar uma tela, destrua o que já há, fica mais fácil de controlar. Em seguida mando limpar a tela.
 gui.destroy()
screen.clear()

gui.title("Simple read and write")
gui.nl();
gui.label("Name:");
dataName = gui.field(1,10,20)

gui.label("Age:");
dataAge = gui.field(1,3,3)

gui.control{type="button", text="Add", handler=saveData}

gui.nl()
data = read()
gui.list(10,32,data)

Digo qual é o título, vou uma linha para baixo (gui.nl) e adiciono as labels e os fields. O detalhe está no botão, em que associo ele a função "saveData" que é o código de gravação do logo acima.

Nova linha, e a função que lê o banco de dados traz os dados em "data" que é atribuido a "gui.list". Quando um registro é gravado, perceba que eu chamo novamente a função que redesenha a interface e faz novamente então a leitura dos dados.

Código na íntegra abaixo:
-- this file write and read data using a palm db

function read()
-- open in read mode
file,NumRegs = io.open("db:/AgesNames", "r+")

-- define a variable
returnData = {}

-- Atention!in DB file, the index begin in 0 (zero)
-- but in table, array..the index begin in 1 (one)
for i=0,NumRegs-1 do
-- open data from index
file:openrec(i) -- begin in zero
-- read this data
data = file:read("*a")
temp = bin.unpack("SB",data)

-- add this data to returning variable
returnData[i+1] = temp[2].."\t"..temp[1]
end

file:close()

-- if are empty, return this information
if(returnData == nil) then
returnData[1] = "No data writed"
end

return returnData
end

function saveData(e, id, arg)

-- retrive values from fields
name = gui.gettext(dataName)
age = gui.gettext(dataAge)
dataToWrite = {name,age}

-- open for write the data base file
file = io.open("db:/AgesNames","w")

-- encode, acording the data tyes
packed = bin.pack("SB",dataToWrite)
-- how many size is this data?
index = file:createrec(string.len(packed))
-- alloc this size on file
file:openrec(index)
-- finaly write
file:write(packed)
-- and close file
file:close()

drawGui()
end


function drawGui()

gui.destroy()
screen.clear()

gui.title("Simple read and write")
gui.nl();
gui.label("Name:");
dataName = gui.field(1,10,20)

gui.label("Age:");
dataAge = gui.field(1,3,3)

gui.control{type="button", text="Add", handler=saveData}

gui.nl()
data = read()
gui.list(10,32,data)
end

drawGui()
gui.main()

É isso amigos, até a próxima, agradescimento especial a Berkant Atay, ilustre desconhecido.
2009-06-14 15:32:00
Este é o antigo Live Helton

Então, português é minha língua mãe, eu não tenho tanto a aprender quanto nos demais idiomas, assim este blog não discute aprendizado do idioma, e sim tópicos randômicos de interesse do dia a dia. Tecnologia, desenvolvimento, um pouco de reflexão crítica, enfim, uma bagunça bem como nossa mente é.
RSS Feed
"A vida é curta demais para ser pequena / The live is so short to be small / Das leben zu kurz sind für kleine sein". (Benjamin Disraeli)