Aprenda Clipper

aprenda clipper

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.

 

As informações on-line dos links abaixo pretendem apenas reciclar e/ou facilitar o aprendizado do programador/aluno.

O autor, não se responsabiliza pelas exigências de definição cobradas, por cada professor dos estabelecimentos de ensino, nas provas.

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.


Lógica de Programação

Você quer iniciar na carreira como desenvolvedor mas não sabe por onde começar? Se sim, você está no lugar certo!

O E-book vai te ensinar a dar o primeiro passo nessa vida como programador, preocupamos em separar os melhores materiais para complementar o assunto tratado, com a vantagem de aprender lógica de programação em menos de um mês de forma leve, explicativa e bem pratica.

👈 Clique na imagem ao lado para saber mais.

Aprender lógica é a base de aprendizado de qualquer linguagem de programação.

Baixe os materiais de estudo gratuitamente abaixo e continue lendo as próximas páginas 👇

*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 clipperIntroduçã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.

// Tudo que vem depois das duas barras é ignorado, apenas comentário.
CLS // LIMPA A TELA
cNOME := SPACE(35) // DEFINE VARIÁVEL cNOME C/ 35 CARACTERES EM BRANCO
@ 01,10 SAY "QUAL É O SEU NOME ?" GET cNOME // Pergunta na linha 1, coluna 10.
READ // LÊ O(S) GET(S) - ESPERA ENTRADA DO TECLADO
// SE FINALIZAR COM [ENTER] ATRIBUI O VALOR À cNOME
// SE SAIR COM [ESC] cNOME PERMANECE = SPACE(35)
@ 02,10 SAY "VOCÊ DISSE QUE ERA "+cNOME // DIZ NA LINHA 2, COLUNA 10, SEU NOME 

 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 aqui.

  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

 

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)

CARACTER Aceita letras e números. Tamanho máximo de 256 caracteres.
DATA Aceita apenas datas.
LÓGICO Aceita 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 de até 64Kb.

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.} )
 

 

COMO PROGRAMAR UM SISTEMA

Um sistema deve ter basicamente:

  • Cadastro;
  • Consulta;
  • Edição;
  • Deleção;
  • Relatórios.

Além disso, todo sistema profissional tem que ter controle de acesso por tipos de usuário. 

Logicamente que um sistema profissional tem muito mais funções. Citei apenas o básico.

Recomenda-se a leitura do "Algoritmos Estruturados - 3ª Ed. 2011 - Farrer,Harry"

Muito bom livro, quem quer programar mesmo de verdade, precisa saber como funcionam os algorítimos em sua forma mais primitiva. Depois recomendo um bom livro sobre arquitetura de computadores, tal como:

Fundamentos da Programação de Computadores - 3ª Ed. 2012 Ascencio,Ana Fernanda GomesCampos,Edilene Aparecida Veneruchi

Finalmente procure um bom livro de MS-SQL Express, com estas leituras você verá que é bem fácil programar.

Se quiser ir mais rápido, veja as dicas do site, tire as dúvidas no fórum e no nosso grupo de Whatsapp.

Multiusuário sem mistério

aprendendoMultiusuá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.

Editando dados

editando dados no ClipperEditando 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!!!

Gravando dados

gravando dados em clipperGravando no banco de dados do Clipper em modo multiusuário. Observe os comentário no exemplo abaixo.

 

// Abre arquivo CLIENTE (banco de dados) e o apelida como 'CLI'.
// Toda vez que tiver um 'CLI->' se refere a um campo do arquivo CLIENTE 
// ou função que rode como se desse um SELECT CLIENTE 
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 que insere um registro em branco 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)                  // se não existir o arquivo
   nHandle := FCREATE(ARQUIVO, 0)  // então, cria o arquivo
   IF FERROR() # 0
      ALERT("Erro"+ALLTRIM(STR( FERROR() ))+": Arquivo não pode ser criado!!!" )
      QUIT
   ELSE
      // deu certo! arquivo criado em branco.
      FWRITE(nHandle, "000000") // grava 000000 no arquivo. O código irá até 999999.
      FCLOSE(nHandle)           // fecha arquivo
   ENDIF
ENDIF
nHandle := FOPEN(ARQUIVO, 66) // abre arquivo
IF FERROR() != 0
   ALERT("Erro"+ALLTRIM(STR( FERROR() ))+": Arquivo não pode ser aberto!!" )
   QUIT
ENDIF
nCOD := VAL( FREADSTR(nHANDLE, 6) ) // lê o arquivo e armazena o conteúdo dele em nCOD
nCOD++  // acrescenta +1
FSEEK(nHANDLE, 0)
FWRITE(nHANDLE, STRZERO(nCOD,6)) // grava novo código no arquivo
FCLOSE(nHANDLE)                  // fecha o arquivo
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.

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.
  • Se você quiser saber como indexar Arrays, veja a página de Dicas!
  • Para salvar e restaurar Arrays para o disco veja as funções na página de Downloads.

TIPOS DE ELEMENTOS DE UM ARRAY 

Um array é uma variável que contém qualquer outro tipo de variável dentro. Um array não terá necessariamente elementos do mesmo tipo, ou seja, um array pode conter strings, números e data em um mesmo array apesar que normalmente tenha elementos do mesmo tipo.

Portanto, se não conhecemos o valor do elemento aARRAY[1] então ele deve ser testado com um VALTYPE(aARRAY[1]). Daí podemos saber se é do tipo caracter, numérico ou data para fazer os ajustes necessários.

APAGAR UM ELEMENTO DO ARRAY 

Você vai precisar usar 2 funções: ADEL() e ASIZE().

ADEL() apaga o elemento, mas o tamanho do array continuará o mesmo, pois o último elemento se tornará vazio.

Portanto, será necessário usar o ASIZE() para encolher o array. Vejamos:

X := ASCAN(aBAKS, {|A| UPPER(A[1]) == "."})
ADEL(aBAKS, X) // APAGA ELEMENTO
aBAKS := aSIZE(aBAKS, LEN(aBAKS)-1) // ENCOLHE ARRAY

Entretanto, caso você esteja compilando com o xHarbour então poderá usar o terceiro parâmetro .T. para fazer a mesma coisa.

X := ASCAN(aBAKS, {|A| UPPER(A[1]) == "."})
ADEL(aBAKS, X, .T.) // APAGA ELEMENTO E ENCOLHE O ARRAY

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...

Essa função automatiza o cadastro de registros, edição, consulta, deleção, busca, filtragem, classificação etc. Você poderá construir sistemas mais rapidamente usando-a para alimentar os bancos de dados e para imprimir os relatórios em qualquer impressora você pode usar o SIBRA.

Criei a função NAVEGAR():

  • Um editor de DBFs do tipo "Tabela" com TBrowse com pesquisa automática

  • Escolha da ordem de indexação (classificação dos dados) dentre todos os índices do DBF com nomes amigáveis 

  • Barra de rolagem fiel (independente do índice usado) 

  • Congelamento de colunas

  • Funções inserir, editar e excluir personalizadas para cada necessidade do programa que usa esta função

  • Funções inserir, editar e excluir padrões, caso não haja nenhuma personalizada definida

  • Edição de campos do tipo memo automaticamente ou definida pelo usuário

  • Permite usar FILTROS com até 2 condições. Campo caractere poder filtrar palavra; campo vazio [empty()] ou não vazio [!empty()] 

  • Lista de filtros usam nomes amigáveis dos títulos das colunas; Limpeza dos filtros não atrapalham a exibição anterior 

  • Permite condições para os parâmetros VALID e WHEN de cada GET do TBrowse

  • Permite colocar máscaras nos campos que também serão usadas nos filtros para funcionar corretamente 

  • Permite (message) exibir mensagem explicativa de status para cada coluna/campo 

  • Campo busca rápida [opcional]

  • Diminua a largura das colunas de campos caractere com a máscara "@S[tamanho]" e deixe a função rolar o restante do texto após 3 segundos de inatividade 

  • Função NAVEGAR2() "clone" para chamar dentro da NAVEGAR() 

  • Compatível com o Clipper, Harbour e xHarbour.  

  • Etc.

ACOMPANHA OS FONTES

§ 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.

Atenção: O download no fim da página 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.

Tela da função NAVEGAR

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!!!

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.

 

O QUE VOCÊ VAI APRENDER COM O TBROWSE
tbrowse
TBROWSE avançado com a função Navegar()
 
Para ter acesso aos links acima você precisa estar logado no site e ter comprado o produto via PayPal no fim da página.
Após a comprovação do pagamento o acesso será automático.
Vá lá e clique no botão Comprar.
 
 
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!!!
 
SUPORTE TÉCNICO PARA TIRAR TODAS AS DÚVIDAS SOBRE O TBROWSE INCLUSO

Suporte técnicoAnderson No Brasil, idioma Português.+55 82 99141-9420
Suporte técnico via Whatsapp c/ AndersonOperadora Claro 21
 PortuguêsEnglishEspañol

Abaixo você pode comprar via PayPal e já ter acesso ao conteúdo pago na mesma hora automaticamente após a aprovação do pagamento.

Anexo: 

https://linguagemclipper.com.br/files/dicnr_0.zip

Aprendendo o tipo de dado HASH {=>}

Vamos desvendar o conceito de Hash! Mas antes, é essencial entender o que é um array. Um array é composto de elementos, um hash também.

O que muda entre um array e um hash é basicamente o endereço (key) do elemento: com Array é um número enquanto que com hash é uma palavra (string). Portanto, hash é um array com chaves não-numéricas.

Essa palavra é chamada de key string ou palavra-chave ou simplesmente key, chave, que é o "endereço" do elemento.

Portanto, você precisa definir a chave e o valor associado a ela. Desta feita, o Hash é similar à um array de duas colunas onde a chave é definida na esquerda e o valor na direita.

Em um hash você precisa saber a chave para obter o valor. Em um array você precisa saber a posição numérica para obter o valor.

Como criar um hash vazio

hPESSOA := {=>} 

ou

hPESSOA := HASH()

EXEMPLO COM UM HASH SIMPLES

Tudo vai ficar mais claro com um exemplo! Vamos definir uma hash hPESSOA que contém 3 tipos de dados: caracter, numérico e data. Vejamos:

FUNCTION MAIN()
SET DATE BRITISH
hPESSOA := {"NOME" => "JOSÉ MANUEL", "IDADE" => 35, "DNASC" => CTOD("25/11/87")}
// Para obter os dados desse hash e testar os tipos de dados faríamos assim:
? hPESSOA["NOME"],          VALTYPE(hPESSOA["NOME"])
? hPESSOA["IDADE"],         VALTYPE(hPESSOA["IDADE"])
? hPESSOA["DNASC"],         VALTYPE(hPESSOA["DNASC"])

Resultado:

hash

 

EXEMPLO DE HASH MULTIDIMENSIONAL

Eis um exemplo de hash com tamanho fixo e dimensões diferentes:

FUNCTION ATUCTR(cCTR)
// FUNÇÃO P/ OBTER OS DADOS ATUALIZADOS DO CONTRATO
hATUCTR := {"VALOR" => {"INICIAL" => EMP->CONTRATADO, "ATUAL" => 0},;
			"VIGENCIA" => {"INICIAL" => EMP->VIGEINI, "FINAL" => EMP->VIGEFIM, "ATUAL" => CTOD("")};
			}
// [...] ROTINA PARA BUSCAR O VALOR ATUAL DO CONTRATO, SE HOUVER
hATUCTR["VALOR"]["ATUAL"] := TA->ATUALIZADO
// [...] ROTINA PARA BUSCAR A PRORROGAÇÃO DO CONTRATO, SE HOUVER
hATUCTR["VIGENCIA"]["ATUAL"] := TA->PRAZO

//  CASO AINDA NÃO HAJA REAJUSTE OU PRORROGAÇÃO, TAIS HASHes SERÃO VAZIOS
RETURN hATUCTR // RETORNA HASH C/ DADOS

 

EXEMPLO DE HASH MULTIDIMENSIONAL DA VIDA REAL

Um hash não vai aparecer tão fácil na vida real como no exemplo básico anterior. Vamos complicar um pouquinho mais agora para ficar mais realista a coisa.

O hash é usado em páginas web, web-services e estão em muitas outras linguagens de programação e costumam trazer um registro do banco de dados.

No exemplo abaixo, o X normalmente é o ID, é o número do registro, tipo um RECNO(). Vejamos então como seria esse exemplo de hash para gravar e ler dados de 3 pessoas:

FUNCTION MAIN()
SET DATE BRITISH
hGRUPO := { "PESSOA" => {{"NOME" => "JOSÉ MANUEL ", "IDADE"=>35, "DNASC"=>CTOD("25/11/87")}, ;
						 {"NOME" => "JOSÉ MAMÃO  ", "IDADE"=>38, "DNASC"=>CTOD("25/11/84")}, ;
						 {"NOME" => "JOSÉ PEREIRA", "IDADE"=>32, "DNASC"=>CTOD("25/11/90")}  ;
                        } ;
          } 
             
? LEN(hGRUPO["PESSOA"])
FOR X=1 TO LEN(hGRUPO["PESSOA"])
    ? hGRUPO["PESSOA"][X]["NOME"], hGRUPO["PESSOA"][X]["IDADE"], hGRUPO["PESSOA"][X]["DNASC"]
NEXT   

Resultado:

Hash Multidimensional

 

Diferença entre KEY e INDEX

Key é o hash. Index é o índice do elemento do hash.

Em um hash multidimensional (nested hash) a função HHASKEY() é muito relevante porque antes de tentar acessar um elemento desse hash primeiro é preciso saber se ele está presente nele senão dará erro de acesso ao array (isso mesmo, array).

Digamos que você leu um arquivo de texto com seções e campos, você pode transformar as seções num hash multidimensional ou hash aninhado (nested hash), cada seção é um hash dentro do hash. Cada campo da seção se torna um elemento desse hash. Todavia, pode ser que nem sempre apareça as mesmas seções no texto então você terá que testar sua presença com HHASKEY() antes de tentar acessar, pois se tentar acessar algo que não exista dará erro. Fiz algo assim com os retornos do ACBR, caiu como uma luva.

 

JSON para HASH, da web para [x]Harbour!

Esta função transforma uma variável hash da web conhecida como JSON em uma variável hash usada no [x]Harbour feita nele próprio sem necessidade de linkar nenhuma LIB.

Observe que ele usa a função StrTran() usada para substituir uma string por outra, traduzindo a sintaxe (convertendo o formato) do JSON para o HASH do [x]Harbour. Vejamos:

Function JSontoHash( cStringJson )
/***
* Converte string formato Json em Hash
*/
Local hJson := {=>}
cStringJson := StrTran( cStringJson,':[','=>{')
cStringJson := StrTran( cStringJson,'":"','" => "')
cStringJson := StrTran( cStringJson,'[','{')
cStringJson := StrTran( cStringJson,']','}')
cStringJson := StrTran( cStringJson,'":null','"=>nil')
cStringJson := StrTran( cStringJson,'":true' ,'"=>.t.' )
cStringJson := StrTran( cStringJson,'":false','"=>.f.')
cStringJson := StrTran( cStringJson,'":','"=>')
cStringJson := StrTran( cStringJson,"\/","/" )
hJSon := &( cStringJson )
Return hJson

Tecnicamente a função acima deve funcionar 100% das vezes, mas caso contrário tente utilizar a lib correspondente da função HB_JSONDECODE().

 

A FUNÇÃO NATIVA HB_JSONDECODE()

Harbour 3.2 e xHarbour 1.2.3 terão já prontas na instalação nativa a função acima para resolver os problemas.

	// RESOLVE ACENTUAÇÃO GRÁFICA DA LINGUA PORTUGUESA
	#ifndef __XHARBOUR	
		JSONCNPJ := Win_OemToAnsi(http:responseText)
	#else
		JSONCNPJ := HB_AnsiToOem(http:responseText)
	#endif
	// CONVERTE JSON PARA HASH
	hb_jsonDecode( JSONCNPJ, @hCNPJA )

No exemplo, pegamos a string do webservice na variável JSONCNPJ e convertemos ela em hash atribuindo à variável hCNPJA.