Introdução ao Processamento de Dados e Programação – Parte 01

Introdução

Vistos os posts anteriores sobre Introdução a Informática (iniciando com Introdução a Informática – Parte 01), agora vamos focar em processamento de dados e programação.

Processamento de Dados

Embora o termo “processamento de dados” esteja intimamente relacionado a “programação”, eu não preciso necessariamente escrever um programa para processar dados. Vamos ver primeiro o que significa “processamento de dados”:

Processamento de dados é uma série de atividades executadas ordenadamente, que resultará em uma espécie de arranjo de informações, onde inicialmente são coletadas informações, ou dados, que passam por uma organização onde no final será o objetivo que o usuário ou sistema pretende utilizar.[1] A obtenção inicial de dados, informações ou propriamento e seu processamento pode ou não ser realizada através de métodos computacionais e tecnológicos, assim como qualquer outra forma de escrita e catalogação. Exemplo: Um álbum de figurinhas é uma forma de processamento de dados, pois a entrada foram dados aleatórios, onde organizados, formaram informações pertinente ao tema desejado.[1] Contudo, para melhor definição, são usados o computadores para realização de tais tarefas, ele é uma máquina que possui um sistema de coleta de dados, um processador, e ferramentas para manipulação das informações.[2][3]

Fonte: Wikipedia

Em suma, uma planilha eletrônica criada usando um aplicativo como o Microsoft Excel, é capaz de fazer processamento de dados. Você define colunas para preenchimento de informações, e colunas calculadas com fórmulas matemáticas, somatórias e afins, e até mesmo cria gráficos baseado nas informações fornecidas pelo usuário ao editar as células da planilha. Mas você não precisou escrever um programa de computador, você no máximo usou algumas funcionalidades disponíveis mediante assistentes visuais da interface do programa. O Microsoft Excel em si é um Software (ou programa de computador), um aplicativo projetado para ser uma ferramenta de criação e edição de planilhas eletrônicas.

Programação

Para atender necessidades específicas, onde um aplicativo não atende aos requisitos necessários, seja por flexibilidade, capacidade ou tipo de processamento desejado, podemos optar por “fazer um software”. Bem, é nesse ponto que as possibilidades ficam literalmente infinitas. Recomendo a leitura complementar do post “A melhor linguagem de programação“, e todas as referências no final deste post 😀 A definição dos 5 passos para a criação de um programa — de um modo geral — descrita na Wikipedia serve como uma luva:

  1. Reconhecer a necessidade de um programa para resolver um problema ou fazer alguma coisa.
  2. Planificar o programa e selecionar as ferramentas necessárias para resolver o problema.
  3. Escrever o programa na linguagem de programação escolhida.
  4. Compilação: tradução do código fonte legível pelo homem em código executável pela máquina, o que é feito através de compiladores e outras ferramentas.
  5. Testar o programa para ter a certeza de que funciona; se não, regressar ao passo 3

Essa abordagem em passos pode muito bem resultar no uso de uma ferramenta ou aplicativo que não envolva codificar ou compilar um programa. Num primeiro momento você acha que precisa construir um programa para resolver um problema ou atender uma necessidade, porém ao procurar as ferramentas necessárias para resolvê-lo, você encontra um aplicativo ou utilitário pronto. Se não for esse o caso, a escolha da linguagem e recursos necessários para resolver o problema passa a fazer parte do problema.

Quem conhece profundamente uma linguagem de programação, consegue resolver de um jeito ou de outro praticamente qualquer necessidade, exceto quando existem restrições ou requisitos são atendidos pela linguagem ou exigidos no projeto.

Para sair um pouco da teoria, vamos partir de uma necessidade específica, que poderia inclusive ser atendida por uma aplicação de mercado já existente, mas que será resolvida usando um programa de computador, que será escrito em várias linguagens. Os objetivos do programa são realizar a tarefa proposta. Para você ter ideia da quantidade de linguagens de programação que poderiam ser utilizadas, recomendo que você visite o site “The Hello World Collection” —  Nessa página existem exemplos de um programa minusculo, onde o objetivo é simplesmente retornar ao usuário ou imprimir na tela a mensagem “Hello World”, escrita em muitas, muitas linguagens de programação — inclusive AdvPL.

Problema proposto

Em uma escola X, o professor Y quer calcular a média da nota da classe inteira, para cada prova bimestral que ele aplica, em cada classe que ele leciona. Após corrigir as provas, ele têm as notas de todos os alunos gravadas em um arquivo de texto, onde cada linha desse arquivo corresponde a nota de um dos alunos, representados por um numero de 0 a 100, sem ponto ou vírgula. O programa de computador deve ser capaz de ler todas as notas deste aquivo, contar quantas linhas tem no arquivo, e calcular a média das notas — somando todas as notas e dividindo pelo número de alunos (linhas) , gerando um resultado inteiro. Vamos partir de um arquivo chamado “notas.txt”, que contém 30 linhas, correspondendo as notas de 30 alunos de uma sala:

87
82
66
85
100
49
83
96
44
54
86
42
65
43
41
85
47
53
84
68
51
77
95
67
85
98
85
48
68
51

Agora, vamos ver como poderíamos fazer isso com algumas aplicações e linguagens de programação — interpretadas, compiladas, em Windows, em Linux, etc !!!

Microsoft EXCEL

Nem precisamos criar um programa. Simplesmente utilizamos o Microsoft Excel, criamos uma planilha em branco, abrimos o arquivo notas.txt usando o bloco de notas do Windows, copiamos o conteúdo inteiro do arquivo para a área de transferência do Windows, posicionamos na primeira linha da primeira coluna da nova planilha vazia, e colamos o conteúdo. Pronto, 30 linhas com a coluna “A” preenchida com a nota. Selecionamos as 30 células desta coluna com o mouse — clicando na célula A1 e arrastando até a A30 — e usamos a aba “Fórmulas”, opção AutoSum -> Average. Pronto, na célula A31 aparece a média da soma das notas, informando o valor 69,5. Podemos editar a célula e alterar a expressão usada para calcular a média, e obter o resultado inteiro — veja abaixo:

Introdução PD 04

 

BATCH SCRIPT ( CMD – Windows ) 

Eu fiquei surpreso, mas esta operação pode ser feita por exemplo usando o “Prompt de Comando” (CMD) do sistema operacional, criando um arquivo de lote (ou Batch Script), por exemplo chamado de “calcnota.bat” — Teste realizado com Windows 10.

@echo off
echo Calculando Notas -- Arquivo NOTAS.TXT
set tot=0 
set lines=0
set media=0
for /f %%a in (notas.txt) do set /a "lines+=1"
for /f %%a in (notas.txt) do set /a "tot+=%%a"
set /a media = %tot%/%lines%
echo Alunos ........... %lines%
echo Soma das notas ... %tot%
echo Media da classe .. %media%
echo.
pause

Resultado obtido:

Introdução PD 01

Python

Python é uma linguagem orientada a objetos e interpretada — você escreve o fonte e chama o aplicativo que interpreta e executa o script. Precisa ter o Python instalado e configurado na máquina. Teste realizado com Python 3.8.0

filepath = 'notas.txt'
with open(filepath) as fp:
  cnt = 0
  notas = 0 
  media = 0
  line = fp.readline()
  while line:
    if len(line) > 0 :
      notas += int(line)
      cnt += 1
    line = fp.readline()
  media = notas / cnt 
  print("Alunos ........... {}".format(cnt))
  print("Soma das notas ... {}".format(notas))
  print("Media da Classe .. {}".format(media))

E, o resultado obtido:

Introdução PD 02

HARBOUR 

O Harbour é um projeto aberto, que deu continuidade a linguagem Clipper, que requer compilação, e gera um código de máquina — ou aplicação executável. O programa abaixo compilou e rodou em uma VM Linux com Ubuntu Server 16 LTS, usando HARBOUR 3.2.0

Function CalcNota()
Local cfilestr, clinha , nMedia 
Local nAlunos := 0 
Local nSomaNota := 0
Local nLinha := 1
cfilestr := memoread('notas.txt')
While !empty( clinha := memoline(cFilestr,,nLinha) )
  nSomaNota += val(alltrim(clinha))
  nAlunos++
  nLinha++
Enddo
nMedia := int( nSomaNota / nAlunos ) 
qout("Alunos ........... "+str(nAlunos))
qout("Soma das notas ... "+str(nSomaNota))
qout("Media da Classe .. "+str(nMedia))
Return

Resultado obtido:

Introdução PD 03

AdvPL

O código abaixo, escrito em AdvPL, roda em qualquer servidor Protheus , qualquer versão, qualquer build, Windows ou Linux. O resultado é mostrado no console do servidor. Como o AdvPL possui a mesma “base” comum de sintaxe e comportamentos do Clipper, o fonte é quase idêntico ao fonte para Harbour — exceto pela USER FUNCTION ser apenas FUNCTION, e o uso da função conout() ao invés da QOut()

User Function CalcNota()
Local cfilestr, clinha , nMedia 
Local nAlunos := 0 
Local nSomaNota := 0
Local nLinha := 1
cfilestr := memoread('notas.txt')
While !empty( clinha := memoline(cFilestr,,nLinha) )
  nSomaNota += val(alltrim(clinha))
  nAlunos++
  nLinha++
Enddo
nMedia := int( nSomaNota / nAlunos ) 
conout("Alunos ........... "+str(nAlunos))
conout("Soma das notas ... "+str(nSomaNota))
conout("Media da Classe .. "+str(nMedia))
Return

// RESULTADO OBTIDO NO CONSOLE DO SERVIDOR

Alunos ........... 30
Soma das notas ... 2085
Media da Classe .. 69

Pontos comuns dos códigos

Cada um desses códigos basicamente realiza as mesmas operações, ou realizam de forma muito parecida. Leitura de um arquivo texto do disco, contagem de linhas, determinar o valor de cada linha, somar os valores em uma variável, dividir o valor das somas pela quantidade de linhas, e mostrar os valores apurados e o resultado da divisão inteira.

Melhorias

Todos os fontes estão corretos e funcionam. MAS, todos eles partem da premissa que o arquivo informado existe, que as notas informadas em cada linha do arquivo não contenham números informados errados — com letras ou espaços no meio dos números ou números maiores que 100.  Logo, uma linha informada errada no arquivo pode gerar um valor que altera a média da classe. Como o objetivo do post é mostrar as diferenças e semelhanças entre diferentes linguagens de programação, eles servem para o propósito. Porém uma versão “final” dessa funcionalidade precisaria de mais algumas linhas e instruções, para prever e tratar eventuais erros nas informações disponibilizadas para este processo.

Um outro ponto interessante, é que todos estes programas foram criados para trabalhar com apenas um arquivo de texto por vez, com um nome fixo, e nenhum deles usou uma interface gráfica para mostrar o resultado. Como se trata apenas de um teste, exemplo ou prova de conceito, isso não é um problema. Mas, se a pessoa que vai usar esse programa não é você mesmo, existem muitos pontos a serem considerados.

Portabilidade e restrições

O exemplo feito em BATCH roda apenas no Windows. Todos os programas exigem que o usuário crie uma pasta na máquina para colocar o script e o arquivo. Java roda em qualquer  máquina, desde que você tenha instalado o “JRE” ( Java Runtime Environment). Python exige que o usuário tenha na máquina dele o Python instalado — com uma versão compatível com o programa que você criou. O Harbour no linux gera um aplicativo executável, mas existe uma dependência com a LIB do Harbour, que deve ser disponibilizada junto do aplicativo ou o usuário tem que instalar um módulo do Harbour na máquina dele. O programa AdvPL pode ser disponibilizado como fonte ou como “patch”, mas exige que o usuário tenha uma instalação funcional do Protheus Server, e como o programa mostra o resultado na tela de console do servidor, não fica nada prático para set utilizado.

Conclusão

Existem muitas formas e muitas linguagens que podem ser utilizadas para atender a uma determinada necessidade. É fundamental para um desenvolvedor especializar-se em uma delas, mas conhecer as demais alternativas, para melhor avaliar suas escolhas.

No próximo post, vamos ver mais alguns fontes, escritos em outras linguagens, para realizar esse mesmo processamento.

Espero que este conhecimento lhe seja útil, e desejo a todos TERABYTES DE SUCESSO !!!

Referências

Manipulação de arquivos em AdvPL – Parte 01

Introdução

Faz algum tempo, eu publiquei um post sobre desempenho em AdvPL, criando uma classe em AdvPL para leitura de arquivos de texto simples (Acelerando o AdvPL – Lendo arquivos TXT). Porém, o exemplo em si já parte da premissa que o programador conhece o que e como funcionam as funções de manipulação de arquivos funcionam. Neste post, vamos ver o que elas fazem “por dentro”, para entendermos como podemos utilizá-las.

Um arquivo no disco

Qualquer arquivo gravado em uma unidade de disco ou em um dispositivo com sistema de arquivos (Drive USB, CD-ROM,etc…) não passa de uma sequência finita de bytes, que representam um conteúdo. Um arquivo (no Windows) normalmente possui um nome e uma extensão, separados pelo caractere “.” (ponto), onde a extensão indica ao sistema operacional qual é o tipo do conteúdo deste arquivo. O arquivo possui um tamanho alocado na unidade na qual ele está gravado — quantos bytes têm o arquivo — além da data e hora da última gravação feita no arquivo, e ainda pode conter atributos especiais.

Uma imagem gravada em disco possui uma representação digital da imagem real, em uma determinada resolução (ou grid de pontos em linhas x colunas), onde o formato utilizado indica ao programa como as sequencias de bytes devem ser tratadas para representar a imagem que o arquivo contém.

Arquivos TEXTO

Um arquivo de texto simples — por exemplo com a extensão “.txt”, nada mais é do que a representação em bytes de todas as letras, números, espaços, pontuação, letras acentuadas e quebras de linha. Normalmente um arquivo texto simples utiliza a representação da tabela ASCII — onde uma faixa de bytes com os valores de 0 a 31 representam caracteres de controle ( quebra de linha, tabulação e outros), do 32 as 127 representam letras maiúsculas e minúsculas, números — representação do número como texto –, espaço em branco, caracteres e símbolos gráficos comuns (exclamação, arroba, dólar, percentual, asterisco, hífen, colchetes, aspas simples e duplas, parênteses, e afins; e por fim a faixa de 128 a 255 é utilizada para representar letras acentuadas, caracteres gráficos e especiais. Esta representação é conhecida como TABELA ASCII.

Existe uma outra padronização mundialmente conhecida de conversão de valores de bytes para caracteres. Chama-se EBCDIC — vide mais detalhes nas referências no final do post — criada pela IBM na década de 60, praticamente junto da padronização do ASCII. Esta tabela foi usada nos equipamentos IBM a partir do Mainframe System/360, usada ainda hoje em Mainframes e Mid-Range Computers da IBM.

Outros formatos de arquivo

Existem vários outros formatos de arquivo, cada formato na verdade é uma especificação de como o seu conteúdo deve ser tratado. A Tabela ASCII é convenção de representação de códigos numéricos para caracteres, criada para padronizar a troca de informações entre computadores. Na prática, dentro do arquivo, eu tenho uma sequência de bytes com valores de 0 a 255.

Texto e Bytes

Vamos partir de alguns exemplos, para deixar mais claro o que tem dentro dos arquivos, e das variáveis do tipo “C” Caractere do AdvPL, usando algumas funções. Por exemplo, se eu quero criar em AdvPL uma variável caractere com as letras A, B e C (maiúsculas, sem acento), eu posso simplesmente fazer uma atribuição direta a uma variável, usando um conteúdo literal.

cTexto := "ABC"

Na área de memória usada para armazenar estes três caracteres, é usado um byte para cada caractere. O valor guardado dentro do Byte é o valor numérico da representação da letra na TABELA ASCII. Por exemplo, a letra “A” é o número 65“B” é 66 e “C” é 67.

Eu poderia escrever isso de outra forma. Por exemplo, usando a função CHR(), que recebe o número do byte e retorna a representação em string do mesmo. Por exemplo:

cTexto := chr(65) + chr(66) + chr(67)

O conteúdo da variável cTexto será exatamente igual nas duas formas de atribuição. Um arquivo texto pode ter algumas diferenças entre sistemas operacionais. Por exemplo, no Windows uma quebra de linha do arquivo é representada pela sequência de bytes chr(13)+chr(10). Já em Linux, é usado apenas chr(10). Logo, eu posso montar a representação de uma linha de um arquivo texto na memória usando:

cLinha := "Uma linha de texto." + chr(13) + chr(10)

Funções de baixo nível de manipulação de arquivo

Existem algumas funções da linguagem AdvPL, projetadas para ler e gravar bytes de qualquer arquivo. Porém, nenhuma destas funções fazem qualquer crítica ao tipo de conteúdo do arquivo — isto é, não importa se o arquivo tem uma imagem, um texto ou um vídeo: Para estas funções, o arquivo é simplesmente uma sequência de bytes. São elas:

  • FCreate – Cria um arquivo.
  • FOpen – Abre um arquivo.
  • FSeek – Reposiciona o ponteiro de dados do arquivo.
  • FRead – Lê bytes do arquivo.
  • FWrite – Grava bytes no arquivo.
  • FClose – Fecha o arquivo
  • FError – Retorna o status de execução da última instrução de arquivo

As funções FCreate() e FOpen() retornam um número identificador, chamado de “Handler de Arquivo”, e todas as demais funções, enquanto o handler não foi fechado pela função FCLose(), recebem este identificador como parâmetro para realizar as operações descritas.

Ponteiro de Dados do Arquivo

O ponteiro de dados é uma espécie de marcador que aponta para um determinado byte do conteúdo arquivo, em uma determinada posição — que também pode ser chamado de “Offset“. O Offset inicia em 0 (zero), correspondendo ao primeiro byte do arquivo. Em um arquivo de tamanho N bytes, o ponteiro de dados pode apontar para uma determinada posição dentro do arquivo, de 0 a N-1, ou pode apontar para o final do arquivo (EOF), correspondendo a posição N.

Qualquer operação de leitura ou gravação sempre é iniciada na posição onde o ponteiro de dados está apontando, e a operação é sempre feito da posição atual em direção ao final do arquivo. Imagine um arquivo no disco como sendo uma sequência de bytes, iniciando da esquerda para a direita.

Função FCreate()

Primeiro vamos criar um arquivo. Usamos para isso a função FCreate(), onde informamos no primeiro parâmetro o caminho e nome do arquivo a ser criado. Em caso de sucesso, a função FCreate() retorna um identificador numérico, chamado de “Handler” do arquivo. Agora, vamos ver alguns detalhes sobre os comportamentos da função FCreate().

  • Caso o arquivo informado como parâmetro contenha uma unidade de disco (drive), o Application Server entende que a criação da tabela será feita pelo cliente AdvPL — SmartClient.
  • Caso o arquivo não contenha o drive ou unidade de disco, o arquivo será criado na máquina onde o Application Server está sendo executado, em uma pasta ou sub-pasta a partir do RootPath (pasta raiz de arquivos do ambiente).
  • Caso o arquivo a ser criado já exista no disco, e não esteja sendo aberto e/ou usado por outro processo, o conteúdo anterior do arquivo é perdido — ele torna-se um arquivo vazio, com 0 (zero) bytes.
  • Caso o arquivo a ser criado não exista, ele é criado vazio — com 0 byte(s).
  • Uma vez que o arquivo tenha sido criado (ou recriado) com sucesso, ele será mantido aberto em modo exclusivo, para leitura e escrita. Isto é, enquanto este programa não fechar o arquivo usando a função FClose(), o arquivo permanece aberto para o processo atual ler e gravar bytes no arquivo, e nenhum outro processo vai conseguir abrir este arquivo, mesmo que seja apenas para leitura.
  • Em caso de falha na criação (ou recriação) do arquivo, o handler retornado é o valor -1 (menos um) , indicando erro. Podemos recuperar a causa do erro chamando a função FError() após executar o FCreate(). A função FError() retorna o código do erro de criação/recriação da tabela, e retorna 0 (zero) em caso de sucesso.
User Function TSTFILE()
Local nHnd 
Local cFile := '\temp\teste.txt'
Local cLine
nHnd := FCreate(cFile)
If nHnd == -1
  MsgStop("Falha ao criar arquivo ["+cFile+"]","FERROR "+cValToChar(fError()))
  Return
Endif

Com o fonte acima, criamos um arquivo vazio, na pasta “\temp\”, que deve ser criada a partir do RootPath do ambiente atual, Esta configuração está presente nos ambientes do Protheus Server, na chave “RootPath” do ambiente.

Como  o arquivo está vazio, o ponteiro de dados do arquivo neste momento não aponta para lugar algum . Agora, vamos gravar uma linha de texto em formato Windows dentro desse arquivo, com quebra de linha.

cLine := "Olá sistema de arquivos" + CRLF

Para quem não conhece, CRLF escrito desta forma no AdvPL — junto e com letras maiúsculas — é um #translate, que retorna os dois bytes ( chr(13) e chr(10) que indicam em um arquivo texto do Windows que deve ser realizada uma quebra de linha. Este recurso está disponível no AdvPL desde que seja utilizado o #include “protheus.ch” — ou o seu equivalente, “totvs.ch”.

Agora, vamos gravar esta informação três vezes no arquivo, em três linhas, uma embaixo da outra, e em seguida fechar o arquivo

FWrite(nHnd,cLine)
FWrite(nHnd,cLine)
FWrite(nHnd,cLine)
FClose(nHnd)

Uma operação de escrita no arquivo é feita sempre a partir de onde está apontando o ponteiro de dados. Como o arquivo não tem nada após ser criado, o ponteiro de dados está em EOF (Final de Arquivo), a operação de escrita no arquivo insere os bytes/caracteres informados no final do arquivo, aumentando o seu tamanho. Após gravar os caracteres, o ponteiro de dados aponta novamente para EOF,

Feito isto, vamos ver exatamente o que foi gravado. Cada linha contém 23 caracteres (ou bytes), mais dois caracteres de controle ( chr(13)  e chr(10) ). Logo, cada linha possui na verdade 25 bytes de informação. Após gravar as três linhas e as respectivas quebras, o arquivo resultante deve ter exatamente 75 byte(s).

Função FOPEN

A função fOpen() é usada para abrir um arquivo. Podemos especificar parâmetros opcionais para definir se o arquivo será aberto para leitura, escrita, ou ambos (leitura e escrita), e ainda é possível especificar o modo de compartilhamento do arquivo — exclusivo, compartilhado, bloqueio de leitura ou bloqueio de escrita. Por default, o arquivo é aberto para leitura exclusiva — nenhum outro processo pode abrir o arquivo para fazer nada, e o processo atual pode apenas ler dados do arquivo.

Seu tratamento de erro é idêntico ao FCreate() — em caso de retorno -1 (menos um), a função FError() contém um código com mais detalhes da falha encontrada.

Veja os detalhes da documentação da função FOpen na TDN. O modo de abertura e compartilhamento da tabela é definido pela soma de dois números das listas de modo de acesso e modo de compartilhamento — apenas um elemento de cada lista. Para usarmos as constantes — que são mais “elegantes” para ler o fonte, devemos fazer um #include “fileio.ch” no início do nosso fonte AdvPL. No nosso exemplo, vamos abrir a tabela no modo default.

nHnd := FOpen(cFile)
If nHnd == -1
   MsgStop("Falha ao abrir ["+cFile+"]","Ferror " + cValToChar(fError()) )
Endif

Caso eu queira, por exemplo, abrir o arquivo em modo compartilhado de leitura e escrita, podemos usar os seguintes parâmetros:

nHnd := FOpen(cFile,FO_READWRITE + FO_SHARED)

Vale lembrar que, ao usar um modo compartilhado que permita escrita no arquivo, dois processos podem tentar escrever na mesma área de dados do arquivo. Se isto acontecer, a última gravação é a que vale. Cabe a aplicação gerenciar os acessos concorrentes ao mesmo Offset do arquivo.

Função FSEEK

Usamos a função FSeek() para mover o ponteiro de Offset de dados do arquivo. Esta função não mexe no arquivo, apenas movimenta o ponteiro de dados do arquivo o parâmetro nOffset informado, e a partir de que ponto do arquivo — ou origem — este Offset deve ser considerado. Veja a documentação oficial completa da FSeek no TDN.

Para entender melhor seu funcionamento, vamos das um exemplo de cada movimentação a partir de uma origem distinta. A origem padrão / default é 0 (zero), indicando que o nOffSet informado é considerado a partir do início do arquivo — posição 0 (zero). Veja abaixo os parâmetros a serem informados para a função.

FSeek( < nHandle >, < nOffSet >, [ nOrigem ] )

O parâmetro nOrigem é opcional. Ele indica a partir de que ponto do arquivo o parâmetro nOffset deve ser considerado. Seu valor default é 0 (zero). Ele pode ser:

  • 0 ( zero) — DEFAULT — Movimenta o ponteiro de dados para a posição informada em nOffSet, a partir do início do arquivo.
  • 1 (um) – Considera o nOffSet como sendo o número de bytes para movimenta o ponteiro de dados a partir de sua posição atual. Neste caso, nOffSet pode ser positivo ( mover o ponteiro para frente) ou negativo (mover o ponteiro para traz).
  • 2 (dois) – Movimenta o ponteiro de dados considerando nOffSet como sendo a quantidade de bytes para deslocar o ponteiro de dados a partir do final do arquivo. Neste caso, nOffSet deve ser 0 (zero) ou um valor negativo.

O retorno da função, independente dos parâmetros utilizados, será a nova posição (OffSet) do arquivo, a partir do início do arquivo. Vamos a alguns exemplos

FSeek( nHnd , 0  [ ,0 ] ) – Posiciona no primeiro byte do arquivo. Retorna o OffSet após o movimento  (zero).

FSeek( nHnd , 3 [ ,0 ]) – Posicional no quarto byte do arquivo. Retorna o OffSet após o movimento (três).

FSeek( nHnd , 0 , 2 ) – Posiciona o ponteiro de dados no final do arquivo (EOF). Neste caso o offset atual corresponde ao tamanho do arquivo — pois para um arquivo de N bytes, o final de arquivo (EOF) é justamente a posição N.

FSeek( nHnd , 5 , 1 ) – Avança o ponteiro de dados 5 bytes para frente da posição atual.

FSeek( nHnd , -5 , 1 )  – Volta o ponteiro de dados 5 bytes para traz da posição atual.

Quando utilizamos o #include “fileio.ch”, ao invés de usar os números de 0 a 2 como terceiro parâmetro da função fSeek(), podemos usar as constantes abaixo:

Número 0       Constante FS_SET         
Número 1       Constante FS_RELATIVE    
Número 2       Constante FS_END

Continuando o programa de exemplo, vamos determinar o tamanho do arquivo e mover o ponteiro de dados para a primeira posição (Offset 0) do arquivo novamente.

nTamFile := FSeek(nHnd,0,FS_END)
FSeek(nHnd,0,FS_SET)

Função FRead()

Agora, vamos ler os primeiros 25 bytes do arquivo. Para isso, usamos a função FRead(). A Documentação oficial está no TDN – FRead. Veja abaixo os parâmetros da função:

FRead( < nHandle >, < cBufferVar >, < nQtdBytes > )

A função recebe em nHandle o identificador do arquivo obtido através da função FCreate() ou FOpen(), uma variável string onde os bytes lidos do arquivo a partir da posição atual do ponteiro de dados, e a quantidade de bytes que devem ser lidas.

Por exemplo, logo após determinar o tamanho do arquivo, vamos ler os primeiros 25 bytes do arquivo:

cBuffer := ""
nRead := FRead( nHnd, @cBuffer, 25 )
conout("Bytes Lidos ... "+cValToChar(nRead))

Primeiro inicializamos uma variável do tipo “C” Caractere no AdvPL com uma string vazia, depois a passamos por referência na função FRead(), seguido do número de bytes que eu quero ler a partir da posição atual do ponteiro de dados do arquivo. A função retorna o número de bytes que efetivamente foram lidos. Caso a leitura termine devido ao final do arquivo ter sido atingido, ou caso tenha havido algum erro, o valor de retorno de FRead() pode ser 0 ou um valor menor que o tamanho que foi solicitado para ler. Em caso de erro na leitura, a função FError() deve conter maiores informações. Se ferror() retornar zero, e o conteúdo lido for menor que o solicitado, então o arquivo efetivamente chegou ao final.

Como eu gravei três linhas de 25 bytes no arquivo, inserindo as quebras de linha, eu seu que ao carregar os primeiros 25 bytes ao arquivo, eu li a primeira linha de dados do arquivo inclusive com a quebra de linha. E, se eu fizer mais duas leituras da mesma forma, eu vou ler fatalmente as linhas 2 e 3 inteiras, respectivamente.

Função FWrite()

Caso eu queira escrever alguma informação no arquivo, eu posso utilizar a função FWrite(), desde que eu tenha aberto o arquivo para escrita. A função FWrite() recebe o identificador ou Handler do arquivo como parâmetro, seguido de uma variável “C” Caractere do AdvPL, contendo uma sequência de caracteres / bytes a serem gravados no arquivo, e pode ainda receber um último parâmetro opcional, permitindo informar quantos bytes do buffer de caracteres informado deve ser salvo. Caso este último parâmetro não seja informado, a string inteira é gravada no arquivo.

De forma similar a FRead(), que começa a leitura a partir da posição atual do ponteiro de dados do arquivo, a função FWrite() começa a gravar os dados a partir da posição atual do ponteiro de dados, em direção ao final do arquivo. Caso o ponteiro de dados esteja situado em um ponto do arquivo com informações já gravadas, estas informações serão perdidas, pois deste ponto em diante a nova string informada como parâmetro será salva. Caso o ponteiro de dados atinga EOF, ou já esteja em EOF, os dados são acrescentados a partir de então, sempre no final do arquivo, aumentando o tamanho do arquivo.

Não há função ou API direta que permita inserir novos dados no meio do arquivo deslocando os demais dados para a frente. E não há função ou API direta que diminua o tamanho de um arquivo. Vamos ao exemplo, continuando o fonte anterior:

nGravado := fWrite( nHnd , replicate('-',23) )

Após ler a primeira linha do arquivo, o ponteiro de dados estará apontando para o primeiro caractere da segunda linha, que é igual a primeira? 23 bytes de dados e os dos bytes finais de quebra de linha. Da forma feita acima, eu literalmente passo por cima da linha escrevendo vários “hifens” sobre a segunda linha, sem invadir o espaço ocupado pela quebra de linha. Se formos ver o arquivo após todas estas execuções, seu conteúdo deve ser:

Olá sistema de arquivos
-----------------------
Olá sistema de arquivos

Se eu escrever por exemplo mais 4 hífens, eu passaria por cima da quebra de linha, e por cima das duas primeiras letras da terceira linha. Sem a quebra de linha, o arquivo ficaria apenas com duas linhas, com o seguinte conteúdo:

Olá sistema de arquivos
---------------------------á sistema de arquivos

Observações finais

  • Se eu abrir um arquivo no modo de escrita, mesmo assim eu ainda consigo ler dados do arquivo.
  • Se eu abrir um arquivo apenas para leitura, a função FWrite() não vão conseguir gravar nada, vai retornar 0 (zero), e a função FError() retornará o código de erro correspondente.
  • Ao usar a função FOpen(), procure especificar o modo de abertura e o modo de acesso, sempre.

Conclusão

Por incrível que pareça, esta é apenas a primeira parte sobre as funções de baixo nível de arquivos em AdvPL. Ainda têm mais coisas para ver no próximo post desta sequência, porém com essa introdução já dá para se ter uma ideia do que é possível fazer em AdvPL.

Agradeço novamente a audiência, as curtidas, compartilhamentos, dúvidas e afins. Desejo a todos TERABYTES DE SUCESSO !!! 

Referências