Aprenda Clipper

 

Caros amigos clippeiros, ultimamente andei procurando na internet apostilas sobre alguns assuntos dos quais não conseguiram atingir seu pleno objetivo, destarte percebi que havia também uma falta de colaboração da minha parte no tocante à Clipper e que deveria esforçar-me para conseguir tempo e disposição para contribuir com um trabalho bem feito no gênero. É uma deliberação delicada passar todo o seu profissionalismo a outra pessoa mesmo quando estamos sendo pagos para isso -imaginem o meu caso...

Contudo, essa "onda" de tudo gratuito... (que todos nós, inclusive eu, temos vários benefícios) mobilizou-me nesse sentido em fornecer tal subsídio.

Não tenho a pretensão de apresentar-se como "perito absoluto" do clipper, sem embargo acredito que, com todo respeito, muitas apostilas, cursos e muitos outros veículos de informação didática e docente em nosso país carecem de um pouco mais de profissionalismo em seu dever... Aí vai!!!

O enfoque do material que pretendo expor é todo voltado à programação multiusuário, até porque não é mais viável se fazer nada em monousuário. Multiusuário serve para um ou vários usuários conectados em rede. Publicarei as matérias aos poucos por motivos óbvios, qualquer assunto sobre programação clipper que mereça um tópico nesta página é só pedir por email.

Compilador Clipper: xHARBOUR

Aviso a todos que não usem cópia pirata do Clipper, ao invés disso, instalem o xHARBOUR. Saibam mais sobre o xHARBOUR clicando aqui.

  

  DICA:

PRESSIONE AS TECLAS <CONTROL> + <F> PARA PESQUISAR QUALQUER PALAVRA NAS PÁGINAS WEB E FACILITAR A BUSCA DO QUE VOCE PROCURA

 

ATENÇÃO: As informações on-line dos links abaixo pretendem apenas reciclar e/ou facilitar o aprendizado do programador/aluno. O autor, Anderson Cardoso, não se responsabiliza pelas exigências de definição cobradas, por cada professor dos estabelecimentos de ensino, nas provas.

 
Introdução ao Clipper
Multiusuário sem mistério
Gravando dados
Editando dados
Tudo sobre Arrays (Matrizes)
Dissecando o TBrowse

Por favor, se você achar que algum tópico não foi bem elucidado e deseja fazer um addendum ou uma ressalva, use o formulário abaixo.

 

 

LINGUAGEM CLIPPER

LÓGICA DE PROGRAMAÇÃO ETC

Clipper em 10 aulas (10Kb TXT)
Comandos c/exemplos (53Kb DOC)
Algoritmo 1 (139Kb HTML)
Algoritmo 2 (23Kb HTML)
Algoritmo 3 (15Kb HTML)
Algoritmo 4 (32Kb DOC)
Algoritmo Estruturado (16Kb DOC)
Algoritmo e Programas (30Kb DOC)
Interpretador de Algoritmos (136Kb)
Lógica de Programação (249Kb PDF)
Análise Estruturada (77Kb DOC)
Engenharia de Software (71Kb DOC)
Curso de MS-DOS (158Kb DOC)

*Estes materiais são Freeware e estão compactados no formato da extensão ZIP.

Registre-se e acrescente você mesmo algumas páginas neste livro "Aprenda Clipper" ensinando alguma coisa para a gente!

Introdução ao Clipper

Introdução ao Clipper

 Para se programar em Clipper, você precisa de um editor de textos tipo o Edit do MS-DOS para escrever os programas. Os mais conhecidos são o Qedit e o Side-Kick pela sua gama de opções na editoração do texto que facilita a vida do programador.

 Os programas são compostos de arquivo de texto e tem por padrão a extensão de arquivo *.PRG.

 Cada linha do programa não necessita de indicadores de final de linha, como se verifica em algumas linguagens com o (;) "ponto e vírgula" (Pascal, Javascript, C etc.). Pelo contrário, se o último caractere de uma linha no Clipper for um "ponto e vírgula", indica que a linha não terminou e que a linha abaixo é continuação da linha de cima.

 Ex:

IF (!EMPTY(cCDEBITO) .OR. !EMPTY(cCCREDITO)) .OR.;
   cCDEBITO # cCCREDITO
   EXIT
ENDIF

  Seria o mesmo que:

IF (!EMPTY(cCDEBITO) .OR. !EMPTY(cCCREDITO)) .OR. cCDEBITO # cCCREDITO
   EXIT
ENDIF

  Só que, para visualizar melhor, você preferiu "quebrar" a linha para não ter que ficar usando a rolagem para a direita para ver o que tem depois.

  Para compreender melhor o programa, sempre utilize endentação das linhas, que nada mais é do que os espaços regulares que são dados no início de cada uma delas para indicar que as informações fazem parte de uma rotina (conjunto de informações) específica a ser executada. Quer dizer que você irá dar espaços regulares nas linhas que estiverem dentro de um Loop (DO WHILE...ENDDO, FOR...NEXT) ou que satisfaçam uma condição (IF...ENDIF, DO CASE...ENDCASE, DO WHILE <condição>...ENDDO)

Ex.:

IF !EMPTY(cCCREDITO)
   nTCRED -= tvCAMPO // ESTORNA VALOR ANTERIOR
   nTCRED += vCAMPO // LANCA VALOR ATUAL
   IF nACAO = 3
      IF SA->( DBSEEK(TMP->CREDITO + DTOS(TMP->DATA) ))
         SA->( LOCKREG())
         SA->CREDITO -= tvCAMPO
         SA->CREDITO += vCAMPO
         SA->( DBUNLOCK())
         SA->( DBCOMMIT())
      ENDIF
   ENDIF
ENDIF

 Observe como é dado os espaços no início de cada linha. Viu como ficou mais legível a visualização?! Desta forma fica muito mais fácil interpretar e entender o programa do que se estivesse tudo junto à margem esquerda, não acha?! Use também linhas em branco para separar segmentos de código que executam um determinada tarefa e abuse de linhas de comentário para lembrá-lo rapidamente o que faz cada rotina do seu programa.

 Os espaços, linhas em branco e linhas de comentários são simplesmente ignorados pelo compilador do Clipper. Não pense que você irá estar economizando bytes do seu programa (*.EXE) ao ignorar a dica do parágrafo anterior. Você já percebeu que não consegue marcar uma linha de comentário no depurador (Debug)?!

 Procure padronizar a programação em letras maiúsculas. Quando for criar variáveis de memória, use a primeira letra da variável igual a primeira letra do tipo da variável, em minúscula, (n = NUMÉRICO, c = CARACTERE, d = DATA, m = MEMO e l = LÓGICO). Por exemplo: dINICIAL := CTOD(""). Você saberá que dINICIAL é do tipo DATA (DATE), em qualquer lugar do programa, porque começa pela letra d.

 Se você está aprendendo uma linguagem, não pense que você precisa decorar a sintaxe de todos os comandos e funções para conseguir programar alguma coisa, ao invés disso mentalize o que aquilo faz e tente imaginar quando irá precisar usar, execute o NG (Norton Guide) quando for programar e aí é só pressionar Shift + F1 para consultar a sintaxe correta. Nem os veteranos sabem a sintaxe de tudo e mesmo sabendo, normalmente preferem não arriscar e consultar o NG, por isso não tenha vergonha de usá-lo, use-o sempre!

 Um Loop ("laço") é um conjunto de informações que são executadas automaticamente mais de uma vez. Os comandos DO WHILE...ENDDO e FOR...NEXT, por exemplo, podem fazer loop, podem fazer executar as informações que estão contidas dentro deles mais de uma vez.

  Linguagem de Alto Nível é a linguagem que se aproxima da linguagem dos homens. É aquela que é mais inteligível aos "olhos dos homens" do que aos "olhos da máquina". Quanto mais "alto nível", mais fácil é de programar e maior o tamanho do programa, porque haverá uma grande "tradução" para a linguagem que a máquina entende.

  Linguagem de Baixo Nível é a linguagem que se aproxima da linguagem das máquinas. É aquela que é mais inteligível aos "olhos das máquinas" do que aos "olhos do homem". Quanto mais "baixo nível", mais difícil é de programar e menor o tamanho do programa, porque haverá apenas uma pequena "tradução" para a linguagem que a máquina entende.

  Compilar um programa consiste basicamente em transformar o seu *.PRG em *.OBJ (arquivos OBJetos), que contém informações em linguagem de máquina. É nesta fase que aparece todos os erros de sintaxe do programa, ou seja, se você digitou alguma coisa errada que não existe, dará erro neste momento. Poderão ocorrer outros erros, mas isto é uma outra estória...

  Linkeditar ou Linkar (do inglês Link, ligar, conectar, vincular) um programa consiste em vincular o seu programa objeto (OBJ) às bibliotecas intérpretes (clipper.lib, extend.lib etc) da linguagem. Todos os comandos do Clipper são transformados em funções (Veja o arquivo STD.CH no sub-diretório /INCLUDE) e estas funções são escritas em uma linguagem de baixo nível, a maioria em C ou Assembler (ASM). Isto quer dizer que as funções que você usa em Clipper, são funções prontas  em C ou ASM para facilitar a sua vida.

  Importante: Quando alguém manda você compilar o programa, este alguém quer dizer: COMPILE e LINKEDITE o programa! Transforme-o em um arquivo executável (*.EXE).

  Para usar o Clipper é preciso definir algumas variáveis ambiente do DOS, que apontam os diretórios onde se encontram cada tipo de arquivo usado pelo Clipper onde ele foi instalado, i.e., arquivos de cabeçalho (*.CH), bibliotecas (*.LIB), objetos (*.OBJ) etc. Usa-se o comando interno do MS-DOS chamado SET para definir tais variáveis e é aconselhável que todas elas estejam no arquivo de lote AUTOEXEC.BAT que é executado automaticamente toda vez que o computador é ligado. 

Ex.:

SET PATH=C:\WINDOWS;C:\WINDOWS\COMMAND;C:\CLIPPER5\BIN;C:\NG;C:\BLINKER
SET CLIPPER=S1,F200
SET INCLUDE=C:\CLIPPER5\INCLUDE
SET LIB=C:\CLIPPER5\LIB;C:\BLINKER\LIB
SET OBJ=C:\CLIPPER5\OBJ;C:\BLINKER\OBJ
SET PLL=C:\CLIPPER5\PLL
SET TEMP=C:\TEMP
SET TMP=C:\TEMP

   Veja mais detalhadamente como instalar o Clipper na página: http://caclipperwebsite.com/todos/node/dicas-cfg-win

  Observe que o linkador Blinker possui algumas variáveis ambiente iguais ao do Clipper, usa-se o ";" (ponto e vírgula) entre os valores da variável. Para saber mais sobre o Blinker, entre em Dicas. Se você não usa o Blinker, ignore o conteúdo de fundo amarelo do exemplo acima.
  Para verificar se estas variáveis estão definidas, digite SET no prompt de comando do MS-DOS (e tecle Enter), aparecerá todas as variáveis de ambiente definidas.

  Para compilar um programa em Clipper, digite CL mais o nome do seu programa. Ex.: CL teste

  CL é um arquivo de lote (*.BAT) que vem com o Clipper e fica no sub-diretório \BIN onde o Clipper foi instalado. Observe que este sub-diretório está no PATH do Autoexec.bat.

  Se você tem o Blinker, mas não sabe usar, baixe o arquivo BLI.BAT na página de Downloads, pois serve como o CL.BAT, exceto que, linkando com o Blinker.

  Todos os arquivos que estão dentro dos diretórios listados na variável ambiente PATH do MS-DOS, separados por ";", podem ser executados em qualquer outro diretório como se o referido arquivo nele estivesse.

  Para se obter o valor de uma variável ambiente no MS-DOS usa-se o "%" no início e no fim da variável. No Clipper usa-se a função GETENV(<variável ambiente>).

  Ex.: Digamos que você gostaria que o diretório C:\UTIL estivesse em seu PATH, mas você não quer alterar o Autoexec.bat e reiniciar a máquina agora e também não quer redigitar o PATH inteiro de novo incluindo o referido diretório, o que você faz? SET PATH=%PATH%;C:\UTIL

  Seu arquivo CONFIG.SYS deve estar, no mínimo, assim:

DEVICE=C:\WINDOWS\HIMEM.SYS /TESTMEM:OFF
DEVICEHIGH=C:\WINDOWS\EMM386.EXE 2048 MIN=1024 RAM HIGHSCAN
DOS=HIGH,UMB
BUFFERS=50
FILES=220
BREAK=ON
SHELL=C:\WINDOWS\COMMAND\COMMAND.COM C:\WINDOWS\COMMAND /E:1024 /P

 

Na última linha, o parâmetro /E:1048 acima reserva 1Kb de espaço ambiente (environment space) para variáveis ambiente. Prevenindo o erro out of environment space quando se está definindo novas variáveis ambiente no MS-DOS. Você pode aumentar esse número para 2048. Não se preocupe com isso se você estiver usando o Windows XP, veja como instalar o Clipper em: http://caclipperwebsite.com/todos/node/dicas-cfg-win

 

Se você deseja criar um sistema em Clipper, entre no sub-diretório SOURCE\DBU do Clipper e observe o que fazem os arquivos *.RMK e *.LNK. Observe também os *.PRG, todos são como grandes funções, começando na primeira linha por FUNCTION NOMEDOPROGRAMA(). Veja também que o programa executado primeiramente contém na sua primeira linha FUNCTION MAIN(). Esta é a melhor forma de compilação porque você não precisa compilar todos os programas de novo se alterar apenas alguns programas, só os programas que estiverem sido alterados serão compilados novamente, poupando-se tempo. Além disso, este método evita o erro: Too many symbols e o Too many labels. Veja a seguir o arquivo DBU.RMK comentado:


// Dbu.rmk
//
// Make file padrão para o utilitário DBU

//
// Copyright (c) 1990-1993, Computer Associates International Inc.
// All rights reserved.
//
// NOTA:
// Para compilar o DBU com o DEBUG (CLD) você deve definir

// o nome da macro DEBUG com o parâmetro /d. Isto pode ser feito assim: 

//
// RMAKE DBU /dDEBUG
//

// Determina se DEBUG é ativado

// Veja mais informações no seu Norton Guide (NG) em: Manutençao de Programas RMAKE.EXE (linha de comando)

#ifdef DEBUG
       //
Se foi ativado inclui parâmetro /b correspondente ao DEBUG

       // Veja mais informações no NG em: Compilador do Clipper CLIPPER.EXE (linha de comando)

       CompOptions := /b /m /n
#else
       CompOptions := /m /n
#end

.prg.obj:
clipper $< $(CompOptions)


// aqui é sempre uma lista de todos os programas que compõem seu aplicativo

// sendo que do lado esquerdo o nome do obj e do lado direito o nome do programa, separados por dois pontos (:)
dbu.obj    : dbu.prg
dbuutil.obj: dbuutil.prg
dbuhelp.obj: dbuhelp.prg
dbuview.obj: dbuview.prg
dbuindx.obj: dbuindx.prg
dbuedit.obj: dbuedit.prg
dbucopy.obj: dbucopy.prg
dbustru.obj: dbustru.prg
dbunet.obj : dbunet.prg


// essas linhas abaixo são desnecessárias, visto que é papel do arquivo *.lnk fazer isso

// não entendo porque existem e você pode criar o seu arquivo *.RMK apenas com as linhas que foram listadas acima.
dbu.exe: dbu.obj dbuutil.obj dbuhelp.obj dbuview.obj \
dbuindx.obj dbuedit.obj dbucopy.obj dbustru.obj dbunet.obj
rtlink @dbu

 

Este arquivo *.RMK é lido pelo RMAKE, já o *.LNK é lido pelo RTLINK, BLINKER etc.

Veja que as barras duplas para o RMAKE é um comentário, já para o RTLINK é a cerquilha (#) que faz esse papel.

Baixe o Norton Guide (NG) em português na página de Downloads.

Agora veremos o arquivo DBU.LNK comentado:

 

# coloque o arquivo principal do seu sistema na primeira linha e depois o resto.

# observe que basta o nome do arquivo sem a extensão

# todos os programas devem ser precedidos por "file"

file dbu
file dbuutil
file dbuhelp
file dbuview
file dbuindx
file dbuedit
file dbucopy
file dbustru
file dbunet
 

# no final ponha as LIBs que serão compiladas com o seu aplicativo

# as LIBs devem ser precedidas por "lib"
lib clipper
lib extend
lib dbfntx
lib terminal
 

Assim você agiliza a compilação do seu sistema.

É tão fácil programar em Clipper que você não precisa de uma estrutura tão complicada para criar um programa simples, veja como ficaria o clássico programa "Olá Mundo":

 

? "Olá, Mundo!"

 

Pronto! Mais nada. Agora é só salvar o arquivo como ola.prg e compilar com CL OLA. Viu como é facil?

 

Agora veja como seria em C++:

 

#include <iostream>
 
int main()
{
   std::cout << "Olá, Mundo!";
   return 0;
}

Em PASCAL:

Program Ola_Mundo;
Uses crt;
Begin
   Writeln('Olá, Mundo!');
End.

 

Em FORTRAN:

 

 PROGRAM HELLO
WRITE(*,10)
10 FORMAT('Olá, Mundo!')
STOP
END

    Em ASSEMBLY:

.model small
.stack
.data
message   db "Olá, Mundo!", "$"

.code

main   proc
   mov   ax,seg message
   mov   ds,ax

   mov   ah,09
   lea   dx,message
   int   21h

   mov   ax,4c00h
   int   21h
main   endp
end main


__________________________

Fonte: Programa Olá Mundo. In: <http://pt.wikipedia.org/wiki/Programa_Olá_Mundo >. Acessado em 01/05/2010.
 

Agora está vendo o que estou falando, não é?! rs

Veremos a seguir os tipos de campos e variáveis do Clipper:

 

Tipos de campos do Clipper

NUMÉRICO

Aceita apenas números com ou sem casas decimais (decimals). O ponto da casa decimal conta no tamanho (width).

Exemplo:  999.99  (width 6, decimals 2)

CARACTERAceita letras e números. Tamanho máximo de 256 caracteres.
DATAAceita apenas datas.
LÓGICOAceita apenas Verdadeiro (.T.) ou Falso (.F.)
MEMO

Aceita letras e números, é como um tipo de campo Caracter com tamanho de até 64Kb. Usado geralmente para armazenar "Observação" ou outras informações que precisem ser detalhadas.

Ao adicionar um campo Memo no seu DBF, é criado um espelho dele com a extensão .DBT. (Se o RDD usado for o nativo do Clipper DBFNTX).

Só é possível editar um campo memo com a função MEMOEDIT().

 

Exemplo: O DBF "clientes" tem um campo memo para observações dos clientes, então irá existir:

CLIENTES.DBF

CLIENTES.DBT

 

Tipos de variáveis do Clipper

NUMÉRICO

Aceita apenas números com ou sem casas decimais (decimals). O ponto da casa decimal conta no tamanho (width).

Exemplo: 

nTOTAL = 0

nCAMPO = 125

CARACTER

Aceita letras e números. Tamanho máximo de 256 caracteres.

Exemplo:

cNOME = SPACE(35)

cCAMPO = "ANDERSON"

DATA

Aceita apenas datas. Considere o uso do comando SET DATE BRITISH para definir o padrão de data igual ao brasileiro dd/mm/aa. Considere também o uso do comando SET EPOCH TO 1980 para resolver o "Bug do Milênio", veja mais em Know-how - Y2K Bug do Ano 2000.

Exemplo:

dDATA = CTOD("")

dHOJE = DATE()

dDTFINAL = CTOD("06/08/2005")

LÓGICO

Aceita apenas Verdadeiro (.T.) ou Falso (.F.)

Exemplo:

lCOMPLETO = .T.

lERRO = .F.

"MEMO"

Variável do tipo Memo não existe, seria uma variável do tipo caracter com mais de 256 bytes que só caberia em um campo Memo.

Exemplo:

mOBS = MEMOREAD("NOTAS.TXT")

ARRAY

Também chamado de matriz ou vetor. São como vários campos agrupados em uma lista. É uma estrutura de dados que contém uma série de dados ordenados, chamados "elementos". Os elementos são referenciados por número ordinal, primeiro elemento é 1, segundo 2... Os elementos podem ser de qualquer tipo, caracter, numérico, data etc. Veja mais detalhes em TUDO SOBRE ARRAYS.

CODEBLOCK

É um tipo especial de variável que armazena um pedaço de código compilado.

Exemplo:

 @ 10,10 SAY "CODIGO:" GET WCOD PICT "999999" VALID;

 EVAL( { || WCOD := STRZERO(WCOD,6), .T.} )
 

 

Espero que tenha satisfeito os visitantes que sentiam falta de informações iniciais introdutórias ao Clipper. Obrigado pela atenção.

AnexoTamanho
Side Kick69.73 KB
QEdit101.05 KB

Multiusuário sem mistério

Multiusuário sem mistério

Para os iniciantes, lembrem que o Clipper só trabalha em rede com unidades mapeadas, isto é, transforme "\\servidor\diretorio\" para "F:", por exemplo. No Windows Explorer, clique com o botão direito do mouse sobre o diretório onde está o sistema no servidor, escolha "Mapear unidade", então associe uma letra de unidade ao diretório. Faça o link apontando para a unidade mapeada, funcionando nela.

Um dos tópicos que "endoidam o cabeção" de muita gente é aprender a programar em modo multiusuário, aprender a usar aliases e abandonar o select, bloquear registros e arquivos etc.

Lembre-se que tudo é muito fácil e simples quando se sabe, então acalme-se: você saberá! (palavra de quem já passou por isso).

Na programação multiusuário é fundamental o conhecimento de funções do clipper, primeira dica: para todo comando do clipper existe uma função equivalente, qualquer dúvida consulte o arquivo de include padrão do clipper /include/STD.CH, é lá onde todos os comandos se tornam operacionais devido a um tipo de tradução tipo "quando ver este comando, traduza para esta função".

Por que o enfoque nas funções? Porque você a partir de agora abandonará o comando select e trabalhará com aliases e os aliases só trabalham com funções.

Exemplo: CLI->(DBSEEK(nCODIGO))

Isto quer dizer: No arquivo de apelido (alias) CLI, pesquise (seek=dbseek()) o valor de nCODIGO, usando o arquivo de indice em uso.

A sintaxe da abertura de um arquivo em modo compartilhado (multiusuario) com a atribuição de um alias, seria o seguinte:

USE CLIENTE ALIAS CLI SHARED NEW

Onde CLIENTE é o nome do arquivo DBF e CLI é o alias (apelido dado a CLIENTE com o parâmetro ALIAS após o nome do arquivo). O resto (SHARED NEW) é sempre igual, é o que faz abrir em modo compartilhado.

Para usar o arquivo aberto em modo compartilhado você precisa usar o alias. Para usar o alias digite o alias -> e ação a ser realizada no arquivo entre parênteses.

Sintaxe: alias->( funcao() )

Note que a função vem sempre entre parênteses.

  Não quero dizer que não se pode usar o vetusto SELECT, é claro que sim, mas você perde bastante objetividade e torna o programa menos legível. Veja um exemplo:

Gravar os campos VALOR, DATA, HORA do arquivo MOVIMENTO e os campos UTILIZADO, ULTCOMPRA e MAIORCOMP do arquivo CLIENTE:

SELECT A

USE MOVIMENTO SHARED NEW

SELECT B

USE CLIENTE SHARED NEW

...

SELECT A

BLOQREG()  // Funcao p/ bloqueio de registro. Explicada a seguir.

REPLACE VALOR WITH nVALOR, DATA WITH dDATA, HORA WITH TIME()

SELECT B

BLOQREG()

REPLACE UTILIZADO WITH nUTILIZADO, ULTCOMPRA WITH DATE(), MAIORCOMP WITH nMAIORCOMP

UNLOCK ALL

VEJA AGORA COMO FICA FÁCIL COM O USO DE ALIASES:

USE MOVIMENTO ALIASES MOV SHARED NEW

USE CLIENTE ALIASES CLI SHARED NEW

...

MOV->(BLOQREG())  // Função p/ bloqueio de registro. Explicada a seguir.

MOV->VALOR := nVALOR

MOV->DATA  := dDATA

MOV->HORA  := TIME()

MOV->(DBUNLOCK())

 

CLI->(BLOQREG())

CLI->UTILIZADO := nUTILIZADO

CLI->ULTCOMP   := DATE()

CLI->MAIORCOMP := nMAIORCOMP

CLI->(DBUNLOCK())

* AMBOS OS CÓDIGOS ACIMA PRODUZEM O MESMO EFEITO

 

RLOCK()

Esta função é usada para bloquear o registro ao ser editado alguma informação no arquivo. Já pensou se dois usuários da rede quisessem editar um mesmo registro ao mesmo tempo??? Para evitar isso, o a linguagem simplesmente não permite isso. Para se editar um registro aberto em modo compartilhado (modo multiusuario) é necessário bloqueá-lo.

A chamada a RLOCK() retorna .T. se a função conseguiu bloquear o registro e .F., caso contrário. Assim, você irá querer fazer uma função para bloquear o registro com certeza de sucesso, por mais simples que seja, como por exemplo:

FUNCTION BLOQREG()

DO WHILE !RLOCK()

ENDDO

RETURN

  Veja este outro exemplo mais implementado que você pode ainda adequar às suas necessidades. Neste caso você deve ter um arquivo DBF de usuários do sistema sempre aberto e em todos os seus arquivos DBF, sem exceção, devem ter o campo LAST_USER para armazenar o código do usuário. Vamos ao exemplo:

FUNCTION BLOQREG()
CURAL := ALIAS() // ALIAS/DBF CORRENTE
lBLOQ := RLOCK()
nTRY := 1
DO WHILE !lBLOQ
   lBLOQ := RLOCK()
   nTRY++
   IF nTRY = 5
      US->(DBSEEK( (CURAL)->LAST_USER) ) // US = ALIAS DO ARQUIVO USUARIOS.DBF
      ALERT("O registro nao pode ser alterado enquanto o usuario ";

            +US->CODIGO+"-"+US->NOME+;

            " estiver usando-o. Espere ele terminar e entao volte!" )
      nTRY := 0

      INKEY(5)
      IF LASTKEY()=27
         EXIT
      ELSE
         LOOP
      ENDIF
   ENDIF
ENDDO
IF lBLOQ = .T.
   (CURAL)->LAST_USER := US->CODIGO // GRAVA O CODIGO DO USUARIO QUE ESTA BLOQUEANDO O REGISTRO
ENDIF
RETURN (lBLOQ)

 Neste novo exemplo, o usuário tomará consciência de quem está usando o registro e poderá entrar em contato com a pessoa, se desejar, e não ficará sem saber o que aconteceu com o sistema paralizado. Eu uso essa função nos meus sistemas com sucesso.

Assim, use BLOQREG() antes de gravar no registro e depois use DBUNLOCK() para liberar o registro para uso. Não esqueça do DBCOMMIT() para descarregar o "CACHE" do disco depois do DBUNLOCK().

 Outra dica importante caso você esteja usando os índices do RDD DBFNTX, o padrão do Clipper:

 Sempre abra todos os índices do arquivo na mesma ordem quando você for gravar dados no arquivo DBF.

 Por exemplo, mesmo que o índice FOCIDADE seja o que você precise para trabalhar primeiro, você usará sempre esse mesmo esquema:

 USE FORNECEDORES ALIAS FO SHARED NEW

 SET INDEX TO FOCOD, FONOME, FOCIDADE, FOMACOMP

 FO->( DBSETORDER(3) ) // Aqui voce diz a ordem que voce quer

 ...

 

E nunca em um programa:

 USE FORNECEDORES ALIAS FO SHARED NEW

 SET INDEX TO FOCIDADE, FOCOD, FONOME, FOMACOMP

  ...

E em outro:

 USE FORNECEDORES ALIAS FO SHARED NEW

 SET INDEX TO FOCOD, FONOME, FOCIDADE, FOMACOMP

 ...

 Porque se dois ou mais usuários inserirem um registro em um mesmo momento, os arquivos podem se corromper.

 Nos próximos tópicos você se tornará mais íntimo destes procedimentos.

 Um problema com arquivos em rede é a integridade dos arquivos, principalmente dos índices, portanto, em todo sistema que criar faça uma rotina de recriação de todos os índices usados, para que caso haja problemas, sejam resolvidos na hora. Lembre-se que para reindexar os índices ninguém na rede poderá estar usando o sistema, ou seja, ter em sua estação algum DBF aberto, assim este procedimento seria desejável todos os dias antes do início de cada jornada de trabalho, como prevenção. Para saber mais sobre problemas dos arquivos de índice, entre na página de KNOW-HOW.

Gravando dados

Gravando dados


A seguir será mostrado um exemplo.

// o (//) é comentário
// Abertura de arquivo
USE CLIENTE ALIAS CLI SHARED NEW
SET INDEX TO CODCLI
// Definicao de variaveis
// técnica: letra inicial da variavel igual ao tipo da variavel: c = caracter
cNOME := SPACE(30)
cEND := SPACE(35)
CLS
@ 10,10 SAY "Nome:" GET cNOME
@ 11,10 SAY "Endereço:" GET cEND
READ
nCOD := NOVOCOD("CODCLI.DAT") // Falarei mais detalhadamente mais adiante
// a chamada ao DBAPPEND() ou ao APPEND BLANK já faz um bloqueio
// automatico no registro, sendo desnecessario a nossa função BLOQREG() (tópico 1)
// até porque, se não fosse, o clipper retornaria um erro.
CLI->(DBAPPEND()) // Função de Append Blank no arquivo CLIENTE (apelidado CLI), veja uma observacao no final.
// Note como são referenciados as variaveis do arquivo: alias->campo do arquivo
CLI->NOME := cNOME
CLI->ENDERECO := cEND
CLI->CODIGO := nCOD
CLI->(DBUNLOCK()) // libera o registro p/ uso
CLI->(DBCOMMIT()) // assegura a integridade do DBF
...
FUNCTION NOVOCOD(ARQUIVO)
IF !FILE(ARQUIVO)
   nHandle := FCREATE(ARQUIVO, 0)
   IF FERROR() # 0
      ALERT("Erro"+ALLTRIM(STR( FERROR() ))+": Arquivo não pode ser criado!!!" )
      QUIT
   ELSE
      FWRITE(nHandle, "000000")
      FCLOSE(nHandle)
 __ENDIF
ENDIF
nHandle := FOPEN(ARQUIVO, 66)
IF FERROR() != 0
   ALERT("Erro"+ALLTRIM(STR( FERROR() ))+": Arquivo não pode ser aberto!!" )
   QUIT
ENDIF
nCOD := VAL( FREADSTR(nHANDLE, 6) )
nCOD++
FSEEK(nHANDLE, 0)     
FWRITE(nHANDLE, STRZERO(nCOD,6))
FCLOSE(nHANDLE)
RETURN (nCOD)

Assim deverá ser a rotina de gravação de um arquivo em modo compartilhado. Agora vamos a algumas observações.

nCOD := NOVOCOD("CODCLI.DAT"))

Os arquivos DBF em rede tem problema de integridade, que devem ser minimizados com medidas de segurança por parte do programador. Refiro-me a integridade dos arquivos de índice principalmente. Já pensou se você atribuisse o novo código desta forma:
 
CLI->(DBGOBOTTOM())
nCOD := CLI->CODIGO + 1

E o seu arquivo de indice estivesse corrompido??? Geraria um código repetidos e comprometeria a consistência do arquivo, que teria dois códigos iguais.

Mas e se você fizesse assim:

nCOD := CLI->(LASTREC()) +1

E quisesse excluir algum registro??? Também não daria certo!

O melhor então é criar um arquivo separado, de texto, que guarde o valor do código atual, para que a chamada da nossa função retorne o novo código, como foi feito na função NOVOCOD().

Observação: Caso você precise de vários DBF's com um campo de código p/ chave primária, uma outra solução é criar uma base de dados com apenas um registro e, vários campos, um campo para armazenar o código atual de cada chave primária do seu sistema. Assim, toda vez que for incluir um registro, você abre a base de dados com as chaves, bloqueia o registro, pega o valor da chave que você quer, soma +1 (mais um), atualiza o campo com o valor da chave atual, desbloqueia o registro e usa este último valor como a chave para o código. Se, no entanto esta base tiver muitos campos, seu sistema provavelmente utilizará muito a base e alguém pode encontrar seu único registro bloqueado por tempo considerado.

 

Append Blank/DbAppend()

Se você possui no seu sistema um programa em que a inclusão de registros é muito grande em rede, tipo registro de vendas, crie uma função para incluir um registro em branco, tratando o erro, pois um Append Blank/DbAppend() falhará se outro usuário tiver bloqueado o banco de dados ou estiver tentando incluir um registro ao mesmo tempo! Veja como ficaria:

CLI->(APPEND()) // Função para o Append Blank no arquivo CLIENTE (apelidado CLI), chamando a função desta forma você já seleciona o arquivo de alias CLI para a função trabalhar.
...
FUNCTION APPEND()
DBAPPEND()
DO WHILE NETERR() // Se falhou de novo, fica tentando...
   DBAPPEND()
ENDDO
RETURN

Outra observação importante: Não adianta tentar bloquear o registro (Rlock()) ou usar sua função de bloqueio de registro antes do DbAppend(), pois estaríamos bloqueando o registro corrente e estamos, na verdade, tentando incluir um novo registro pelo sistema, destarte, é apropriada apenas o exemplo acima.

Editando dados

Editando dados


A regra do Clipper é bloquear o registro antes de gravar os dados, mas observe que há mais coisas entre o céu e a terra do que prega nossa mera filosofia...!

Veja um exemplo:

// o (//) é comentário
// Abertura de arquivo
USE CLIENTE ALIAS CLI SHARED NEW
SET INDEX TO CODCLI
// Definicao de variaveis
// técnica: letra inicial da variavel igual ao tipo da variavel: c = caracter
cCODCLI := SPACE(6)
DO WHILE .T.
   CLS
   @ 10,10 SAY "Código do Cliente:" GET cCODCLI VALID EVAL( { || cCODCLI := STRZERO(cCODCLI,6), .T.} ) // Entendeu o valid?! Não? Veja na página de Know-How.
   READ
   IF LASTKEY() = 27 //ESC
      EXIT
   ENDIF
   IF !CLI->(DBSEEK(cCODCLI)) // ! é .NOT.
      ALERT("CODIGO NAO ENCONTRADO")
      LOOP
   ENDIF
   CLI->(DBLOCKREC()) // Função bloqueia o registro em uso (necessário em rede ao alterar registro), veja a função mais abaixo e comentários sobre a posição da mesma. Insira sempre esta função imediatamente antes de disponibilizar os dados p/ alteração.
   cCLI := CLI->NOME
   cEND := CLI->ENDERECO
   // outras variáveis...
   @ 11,10 SAY "CLIENTE:"  GET cCLI
   @ 12,10 SAY "ENDERECO:" GET cEND
   // outros gets...
   READ
   @ 20,10 SAY "CONFIRMA ALTERACAO? (S/N)" GET cSN VALID UPPER(cSN) $ "SN"
   READ
   IF cSN = "N"
      LOOP
   ENDIF
   // Não ponha o CLI->(DBLOCKREC()) aqui! (Veja explicação).
   CLI->NOME     := cCLI
   CLI->ENDERECO := cEND
   CLI->(DBUNLOCK()) // libera o registro p/ uso
   CLI->(DBCOMMIT()) // assegura a integridade do DBF
ENDDO 

...

FUNCTION DBLOCKREC() // Esta função é definida pelo usuário, não é nativa do Clipper!
DO WHILE !RLOCK()
ENDDO
RETURN

Explicação: Você não deve bloquear os registros imediatamente antes de alterar os campos do registro porque você incidirá no problema da atualização perdida. Exemplo: duas estações estão executando o mesmo programa; caso ambas leiam o mesmo registro e introduzam mudanças a serem gravadas, qual será o estado do registro? Que mudanças do usuário foram gravadas? Resposta: As alterações do usuário que gravou pela última vez, a do primeiro se perde, são sobreescritas!

Comentários: O nosso programa acima resolve o problema da atualização perdida, mas já pensou se alguém desse nosso programa começasse a editar um registro e depois se afastasse de sua máquina, digamos, para tomar uns três cafezinhos ou fosse para casa?! Os dados ficariam inacessíveis aos outros por muito tempo!!!

 

Agora veja um exemplo PROFISSIONAL:

Use um campo assinatura

Inclua um campo novo em cada base de dados de seu sistema chamado de assinatura (assinatura, N, 3, 0). Ele existirá apenas para permitir que a codificação da atualização verifique se os campos foram alterados. Veja como ficaria o nosso programa exemplo supracitado reconstruído:

// o (//) é comentário
// Abertura de arquivo
USE CLIENTE ALIAS CLI SHARED NEW
SET INDEX TO CODCLI
// Definicao de variaveis
// técnica: letra inicial da variavel igual ao tipo da variavel: c = caracter
cCODCLI := SPACE(6)
DO WHILE .T.
   CLS
   @ 10,10 SAY "Código do Cliente:" GET cCODCLI VALID EVAL( { || cCODCLI := STRZERO(cCODCLI,6), .T.} ) // Entendeu o valid?! Não? Veja na página de Know-How.
   READ
   IF LASTKEY() = 27 //ESC
      EXIT
   ENDIF
   IF !CLI->(DBSEEK(cCODCLI)) // ! é .NOT.
      ALERT("CODIGO NAO ENCONTRADO")
      LOOP
   ENDIF
 
   CLI->(DBLOCKREC())
   CLI->ASSINATURA++
   nASS := ASSINATURA // Representa as informacoes atuais do registro (p/ comparar depois).
   CLI->(DBUNLOCK())
   CLI->(DBCOMMIT())
 
   cCLI := CLI->NOME
   cEND := CLI->ENDERECO
   //outras variáveis...
   @ 11,10 SAY "CLIENTE:" GET cCLI
   @ 12,10 SAY "ENDERECO:" GET cEND
   //outros gets...
   READ
   @ 20,10 SAY "CONFIRMA ALTERACAO? (S/N)" GET cSN VALID UPPER(cSN) $ "SN"
   READ
   IF cSN = "N"
      LOOP
   ENDIF
 
   IF nASS # CLI->ASSINATURA
      ALERT("Registro já alterado por outro usuário, suas alterações serão perdidas!")
      LOOP
   ENDIF
 
   CLI->(DBLOCKREC())
   CLI->NOME := cCLI
   CLI->ENDERECO := cEND
   CLI->ASSINATURA := 0
   CLI->(DBUNLOCK()) // libera o registro p/ uso
   CLI->(DBCOMMIT()) // assegura a integridade do DBF, gravando do Buffer para arquivo.
ENDDO 

Acredite se quiser, mas muitas grandes SoftHouses ainda não fazem isso... FAÇA A DIFERENÇA!!!

Tudo sobre Arrays

Arrays (Matriz / Vetor)


 

É uma estrutura de dados que contém uma série de dados ordenados, chamados "elementos". Os elementos são referenciados por número ordinal, primeiro elemento é 1, segundo 2... Os elementos podem ser de qualquer tipo, caracter, numérico, data etc.

Você lembra do conceito de Matriz quando nas épocas de colégio, em Matemática??? Pois é, a mesmíssima coisa!

Um Array ou matriz é uma variável em clipper que armazena vários valores, onde tais valores são armazenados e consultados através de sua posição na matriz.

Vejamos um exemplo de uma matriz numérica de 4x3 (4 linhas e 3 colunas):

Que poderia ser representada assim:

Onde cada anm é um valor da matriz. Por exemplo, se você quisesse saber o valor de a32 na matriz B você encontraria o número 25. Analogamente os outros valores.

 

Vejamos outro exemplo, agora de uma matriz de uma só coluna, de 3x1 (3 linhas e 1 coluna), também conhecida de Vetor (só quando uma coluna):

Que poderia ser representada assim:

Onde cada anm é um valor da matriz. Por exemplo, se você quisesse saber o valor de a21 na matriz C você encontraria o número 7. Analogamente os outros valores.

Agora que você entendeu bem a idéia, vamos traduzir isso para linguagem Clipper:

A matriz B poderia ser contruída em Clipper da seguinte maneira, compare com a imagem:

Em Clipper:Na Matemática:
// definindo a matriz:
B := ARRAY(4,3)
// atribuindo valores
B[1][1] := 10
B[1][2] := 20
B[1][3] := 40
B[2][1] := 15
B[2][2] := 35
B[2][3] := 42
B[3][1] := 53
B[3][2] := 25
B[3][3] := 37
B[4][1] := 12
B[4][2] := 91
B[4][3] := 33
Outra forma em Clipper, atribuição dinâmica de uma matriz:
Em Clipper:Na Matemática:
// definindo a matriz:
B := {}
// atribuindo valores
AADD(B, {10,20,40})
AADD(B, {15,35,42})
AADD(B, {53,25,37})
AADD(B, {12,91,33})

Para consultar o valor de a32 da matriz B em clipper você poderia usar:

? B[3][2]

 

A matriz C poderia ser contruída em Clipper da seguinte maneira, compare com a imagem:

Em Clipper:Na Matemática:
// definindo a matriz:
C := ARRAY(3)
// atribuindo valores
C[1][1] := 3
C[1][2] := 7
C[1][3] := 4
Outra forma em Clipper, atribuição dinâmica de uma matriz:
Em Clipper:Na Matemática:
// definindo a matriz:
C := {}
// atribuindo valores
AADD(C, 3)
AADD(C, 7)
AADD(C, 4)

 

Para consultar o valor de a21 da matriz C em clipper você poderia usar:

? C[2]

Atenção: Se você colocar uma posição além do tamanho do array, o sistema retornará um erro de BASE/1132 Bound error: array access porque o elemento referenciado não existe. Por exemplo, se fizéssemos ? C[4] na matriz C citada acima, suscitaria este erro, pois C[4] não existe!

Ok, agora vamos para outro passo.

Para se buscar um valor dentro do Array, usa-se a função ASCAN(), vamos recordar sua sintaxe:

ASCAN(<aARRAY>, <expPROCURA>,[<nINICIO>], [<nCONTAGEM>])

Onde:
 
aARRAY = Array onde se fará a pesquisa.
 
expPROCURA = Expressão de procura, pode ser um valor de qualquer tipo (caracter, numerico, data ou lógico) ou um bloco de código (code block).
 
nINICIO = Opcional. Valor númerico que representa o elemento inicial de pesquisa, qual a posição do Array que deve iniciar a pesquisa. Se omitido, o valor padrão será 1 (inicio do array)
 
nCONTAGEM = Opcional. O número de elementos que irá pesquisar a partir de nINICIO. Se omitido, o valor padrão será o tamanho do array ( LEN(aARRAY) ).

ASCAN() retorna um valor numérico representando a posição do elemento procurado caso este seja encontrado, caso contrário, retornará 0 (zero).

 

Agora veja alguns dos diversos modos de buscar um valor dentro de um Array, usando a função ASCAN() do Clipper:

Exemplo 1: Busca simples.

aArray := { "Tom", "Mary", "Sue" }

? ASCAN(aArray, "Mary") // Resultado: 2
? ASCAN(aArray, "mary") // Resultado: 0
? ASCAN(aArray, { |x| UPPER(x) == "MARY" }) // Resultado: 2

Exemplo 2: Verificar quantas ocorrências de um valor dentro do Array.

aArray := { "Tom", "Mary", "Sue","Mary" }
 
nStart := 1 // Armazena última posição do elemento no Array
nAtEnd := LEN(aArray) // tamanho da matriz
DO WHILE (nPos := ASCAN(aArray, "Mary", nStart)) > 0
   ? nPos, aArray[nPos] // mostra posicao e valor.
   // Atribui nova posicao inicial e testa
   // com tamanho da matriz (evitar erro).
   IF (nStart := ++nPos) > nAtEnd
      EXIT
   ENDIF
ENDDO

-------

Obs.: A linha: DO WHILE (nPos := ASCAN(aArray, "Mary", nStart)) > 0

é o mesmo que:

nPOS := ASCAN(aArray, "Mary", nStart)
DO WHILE nPOS > 0
   nPOS := ASCAN(aArray, "Mary", nStart)

 

Exemplo 3: Este exemplo procura uma matriz bi-dimensional usando um bloco de código (code block). A função retornará a posição da linha do elemento procurado. Note que o parâmetro aVal no bloco de código é uma matriz e que aVal[2] significa coluna 2 da matriz (alí você colocará o número da coluna que você irá procurar):

aArr:={}
CLS
AADD(aArr,{"um",   "dois"  })
AADD(aArr,{"três", "quatro"})
AADD(aArr,{"cinco","seis"  })

? ASCAN(aArr, {|aVal| aVal[2] == "quatro"}) // Retornará 2.

 

Dissecando o TBrowse

DISSECANDO O TBROWSE

 

Prezados Clippeiros,  

Sempre quis aprender essa magnífica ferramenta TBrowse do Clipper, mas lidar com objetos e termos esquisitos como "estabilizar" etc. desmotivam muito quem está aprendendo... Até que um dia resolvi dar um basta nisso, reuni esforços e hoje entendo perfeitamente tudo!

Pesquisei em muitos sites de clipper e o máximo que encontrei foi explicação sobre cada pedaço do TBrowse, isso a maioria das pessoas já tem, mas o que sempre quisemos saber realmente é como juntar toda aquela parafernália de informações e usar nos programas!!!

Infelizmente, tive que gastar muito tempo em pesquisa, raciocínio e testes... muitos testes, até aperfeiçoar esta técnica TBrowse, mas não desanime, eu te trago agora uma boa notícia: você não vai precisar passar por isso também! Afinal, se já existe o conhecimento que você precisa no mundo, para quê passar anos tentando reinventar a roda???

Como todo bom clippeiro, quanto mais podermos automatizar nosso trabalho, melhor, não é?! Por isso mesmo desenvolvi uma função com o TBrowse usando o máximo da capacidade modus operandi...

Resultado:

 

NOVA FUNÇÃO: NAVEGAR versão 2010! (com os fontes)

Mais estável e poderosa!

§ 2º O órgão ou entidade responsável pela retenção deverá enviar à unidade da SRF do local de seu domicílio, relação, em meio digital, contendo o nome ou a razão social, o número de inscrição no CNPJ e os valores pagos no período de 01 de janeiro a 31 de dezembro de cada ano, das entidades de que trata o caput, até o último dia útil do mês de março do ano-calendário subseqüente ao dos pagamentos efetuados. (Revogado pela IN SRF nº 706, de 9 de janeiro de 2007)

Já que a DICNR foi revogada pela IN/SRF 706/2007, estarei disponibilizando os fontes do programa gratuitamente com o pacote "Dissecando o TBrowse" também como brinde. Este programa utilizou a função xNAVEGAR.

Conheça melhor o DICNR no meu site da Numerabilis Contabilidade: http://www.numerabilis.cnt.br/dicnr.php

 

DICNR  ( 389Kb)

(Atenção: O download acima só contém os binários do programa, os fontes estão no pacote "Dissecando o TBrowse")

*Fonte é a programação que deu origem ao aplicativo, ao binário.

 

Naturalmente, tenho estas funções para mim como meu maior "tesouro" e, como tal, a guardei há muito tempo à sete chaves... Mas, hoje, estou resolvendo desenterrar o "tesouro" e oferecê-lo somente àqueles que valorizam o conhecimento e querem devorar toda a capacidade do TBrowse e ter para si uma função que, realmente, além de poupar muito esforço e tempo, refletirá um tom de "expert" e "know-how" notável em qualquer trabalho onde for usado.

Na página seguinte você irá baixar um arquivo com a função para usar em seus sistemas, ver um exemplo pronto para ser compilado (Exemplo com a Visual Lib 2) e, além disso, verá a listagem do programa na tela com explicações sobre cada comando ao passar o mouse em cima (eu chamo isso de "dissecando o tbrowse"), destarte você terá muito mais que uma excelente função, terá todo o know-how aplicado e poderá desenvolver funções ainda mais poderosas com o que aprendeu a partir do código fonte fornecido. Se você já é um programador veterano, poupe tempo, trabalho e esforço (que só você sabe que tem) e baixe logo algo já pronto... não é melhor do que reinventar a roda?! Vem com os fontes!!!

Para entrar solicite o

nome de usuário e a senha

clicando aqui.

Ensinar

PS.: O que você vai aprender e a função que vai obter não se encontra em livros, revistas ou magazines. É um material exclusivo do CACLIPPER Website, confeccionado por Anderson Cardoso Silva, Maceió/AL, Brasil.

 

Aprender

TBrowseDB
:addcolumn
TbColumnNew
:COLCOUNT
:GETCOLUMN
:PICTURE
:SKIPBLOCK
:gobottomblock
:gotopblock
:STABLE
:FORCESTABLE()

:COLPOS

:down()
:up()
:pageDown()
:pageUp()
:goTop()
:goBottom()
:right()
:left()
:home()
:end()
:panLeft()
:panRight()
:panHome()
:panEnd()
:FREEZE
:REFRESHCURRENT()
:ColorRect()
:REFRESHALL()
:rightVisible
:leftVisible
:Hilite()
:refreshCurrent()
:ROWPOS

  Se você não conhece os comandos acima, você precisa DISSECAR O TBROWSE hoje mesmo!!!

 

 

Enviar para um amigo!