Esse sistema trabalha não apenas com um mas com vários arquivos texto, que seguem o mesmo esquema de "aliases" do Clipper com arquivos DBF. Cada arquivo texto é indexado e seus "entry points" são armazenados, a fim de permitir o acesso randômico a qualquer linha, independentemente da largura que ela tenha. Além disso, há uma função que retorna o tamanho da maior linha existente, dispensando uma "varredura" extra, o que facilita a vida dos que pretenderem fabricar visualizadores de texto (ex: preview de relatórios).
Infelizmente, dada a falta de tempo, é um projeto inconcluso. Faltaram: a inserção de linhas e a criação de comandos, a fim de facilitar mais ainda o uso. Notem que existe uma função chamada txAddLine(), mas ela está pela metade, incluindo linha apenas no final do arquivo (append).
O arquivo de cabeçalho com algumas constantes:
Código: Selecionar todos
#define _kTXTSYS_NOERROR 0
#define _kTXTSYS_FILENOTFOUND 1
#define _kTXTSYS_OPENERROR 2
#define _kTXTSYS_CREATEERROR 3
#define _kTXTSYS_READERROR 4
#define _kTXTSYS_WRITEERROR 5
#define _kTXTSYS_ALREADYOPEN 6
#define _kTXTSYS_SELECTERROR 7
#define _kTXTSYS_ALIASNOTFOUND 8
#define _kTXTSYS_NOTSELECTED 9
// Modos de acesso e compartilhamento dos arquivos
#define _kTXTSYS_ACCESS_READ 0 // leitura
#define _kTXTSYS_ACCESS_WRITE 1 // escrita
#define _kTXTSYS_ACCESS_READWRITE 2 // leitura e escrita (default para a abertura)
//
#define _kTXTSYS_SHARING_EXCLUSIVE 16 // acesso exclusivo (default na criação do arquivo)
#define _kTXTSYS_SHARING_DENYWRITE 32 // proibição de escrita
#define _kTXTSYS_SHARING_DENYREAD 48 // proibição de leitura
#define _kTXTSYS_SHARING_DENYNONE 64 // compartilhamento totalCódigo: Selecionar todos
//
//
// TXT Sub-System v1.0
// --------------------------------
// (c) 04/2004, Paulo Buzinello
// Londrina/PR
// Brasil
// paulo@buzinello.com
//
// ---------------------------------------------------------
// Sub-sistema de controle de acesso, para leitura e escrita
// em arquivos de texto, de forma semelhante ao sistema DBF,
// por acessar randomicamente suas linhas através de índice.
// ---------------------------------------------------------
//
// Funções Públicas
// ------------------------------------------
// txAddLine() - adiciona uma linha no arquivo
// txClose() - fecha o arquivo do texto ativo ou indicado
// txCloseAll() - fecha todos os arquivos, em todas as áreas
// txError() - informa qual o último erro registrado
// txGetLine() - lê uma linha inteira do texto ativo
// txLines() - informa a quantidade de linhas de um texto
// txMaxCol() - informa a largura da maior linha existente
// txNextMark() - encontra a próxima marca de tabulação
// txOpen() - abre um arquivo texto, indexando-o
// txSetArea() - seleciona uma área de texto
// txSize() - informa o tamanho do texto ativo
//
//
#include "__sysmac.ch"
#include "__txtsys.ch"
static aFCB := {} // bloco de controle de arquivo (descrito abaixo)
static nArea := 0 // área de texto atualmente selecionada
#define _FCB_ALIAS aFCB[nArea][ 1] // nome da área
#define _kFCB_ALIAS 1 //
#define _FCB_FILENAME aFCB[nArea][ 2] // nome completo do arquivo
#define _FCB_FILEHAND aFCB[nArea][ 3] // número da alça de manipulação
#define _FCB_FILESIZE aFCB[nArea][ 4] // tamanho do arquivo
#define _FCB_PREGETLIN aFCB[nArea][ 5] // bloco de código de preparação de leitura de uma linha
#define _FCB_PRESETLIN aFCB[nArea][ 6] // bloco de código de preparação de escrita de uma linha
#define _FCB_LINS aFCB[nArea][ 7] // quantidade de linhas (obtida na carga)
#define _FCB_COLS aFCB[nArea][ 8] // largura da linha mais extensa
#define _FCB_FOCUS aFCB[nArea][ 9] // número de ordem da linha atualmente focalizada
#define _FCB_MARKS aFCB[nArea][10] // matriz com os números de linhas marcadas para tabulação
#define _FCB_POINTERS aFCB[nArea][11] // string que armazenará os ponteiros de acesso
#define _kFCB_SIZE 11 //
// ---------------------------------------------------------
// Informações sobre os ponteiros de acesso rápido às linhas
// ---------------------------------------------------------
// A string _FCB_POINTERS conterá os ponteiros usados para o
// acesso direto às linhas no arquivo. Esta será povoada com
// a abertura do arquivo. Seu tamanho limite também limitará
// a capacidade de leitura do arquivo. Cada ponteiro terá um
// tamanho fixo, definido por _kPOINTER_LEN. Como a variável
// Clipper está limitada em 64Kb, o maior arquivo que poderá
// ser manipulado, terá sua quantidade de linhas limitada ao
// resultado da divisão de 65535 por _kPOINTER_LEN. Caso seu
// valor seja 4 (o default) por exemplo, teremos o máximo de
// 16.383 linhas. Já que os ponteiros estarão armazenados no
// formato alfadecimal super-extendido, o tamanho do arquivo
// estará limitado a 78.074.895 bytes.
// Com relação ao buffer de leitura: o tamanho de 4KB parece
// ser apropriado, uma vez que dificilmente uma linha terá a
// largura superior a 4.096 colunas.
// Em resumo: os valores, relativamente grandes, deverão ser
// suficientes para atender a grande maioria das aplicações.
// ---------------------------------------------------------
//
#define _kBUFFER_LEN 4096 // largura máxima de uma linha e também do buffer de leitura
#define _kPOINTER_LEN 4 // largura de cada ponteiro de acesso
// tamanho máximo do texto: ~~~~(aSE) == 78.074.895(d) bytes
//**************************************************************************************************
static function BuildIndex() // O objetivo desta função é calcular a quantidade de linhas do
local cBuffer := Space(_kBUFFER_LEN) // arquivo e montar um índice com os ponteiros de acesso rápido
local nFPoint := 0 // a essas linhas, dentro do arquivo. Cada ponteiro é um número
local cRead := "" // na base alfadecimal de largura fixa, definida pela constante
local i // _kPOINTER_LEN.
local n
//
// Inicialização do ponteiro do arquivo e variáveis
FSeek(_FCB_FILEHAND,0,0)
_FCB_LINS := 0
_FCB_COLS := 0
_FCB_MARKS := {}
_FCB_POINTERS := ""
//
// Leitura integral do arquivo com o armazenamento do ponteiro de acesso a cada linha
while (n := FRead(_FCB_FILEHAND,@cBuffer,_kBUFFER_LEN)) > 0
cRead += Left(cBuffer,n)
while (i := At(EOL,cRead)) > 0
_FCB_LINS++
_FCB_POINTERS += Dec2AlfaSE(nFPoint,_kPOINTER_LEN)
_FCB_COLS := if(_FCB_COLS < i-1, i-1, _FCB_COLS)
if Left(cRead,Len(PrtTabMark())) = PrtTabMark()
AAdd(_FCB_MARKS,_FCB_LINS)
end
nFPoint += i+Len(EOL)-1
cRead := SubStr(cRead,i+2)
end
cBuffer := Space(_kBUFFER_LEN)
end
//
// O último ponteiro (extra) será necessário apenas para o cálculo do tamanho da última linha
if Empty(cRead)
_FCB_POINTERS += Dec2AlfaSE(nFPoint-2 ,_kPOINTER_LEN)
else
_FCB_LINS++
_FCB_POINTERS += Dec2AlfaSE(nFPoint ,_kPOINTER_LEN)
_FCB_POINTERS += Dec2AlfaSE(nFPoint+Len(cRead),_kPOINTER_LEN)
end
return VOID
//**************************************************************************************************
static function GetIndex(cAlias)
return AScan(aFCB,{|aFile|aFile[_kFCB_ALIAS]==Upper(cAlias)})
//**************************************************************************************************
function txAddLine(cLine,nOrder,xAlias)
local nLast := nArea
local lRet
*
default xAlias to nArea
default nOrder to 0
*
txError()
if (lRet := txSetArea(xAlias))
if nOrder = 0 .or. nOrder > _FCB_LINS
// Adicionar a nova linha no final do arquivo
_FCB_LINS++
_FCB_POINTERS += Dec2AlfaSE(nFPoint ,_kPOINTER_LEN)
_FCB_POINTERS += Dec2AlfaSE(nFPoint+Len(cRead),_kPOINTER_LEN)
else
//
// Adicionar a nova linha numa determinada posição do arquivo,
// deslocando todas as linhas já existentes para o final deste
end
end
return lRet
//**************************************************************************************************
function txClose(xAlias)
local lRet
*
if (lRet := txSetArea(if(xAlias=VOID,nArea,xAlias)))
FClose(_FCB_FILEHAND)
if Len(aFCB) = 1
aFCB := {}
else
ADel(aFCB,nArea)
ASize(aFCB,Len(aFCB)-1)
nArea := RetMin(nArea,Len(aFCB))
end
GarbagColl()
end
return lRet
//**************************************************************************************************
function txCloseAll()
while txClose(1)
end
txError()
return VOID
//**************************************************************************************************
function txError(nError)
static nLstError := _kTXTSYS_NOERROR
if nError = VOID
nError := _kTXTSYS_NOERROR
Exchange(@nLstError,@nError)
else
nLstError := nError
end
return nError
//**************************************************************************************************
function txFHandle(xAlias)
return if(txSetArea(if(xAlias=VOID,nArea,xAlias)), _FCB_FILEHAND, -1)
//**************************************************************************************************
function txGetLine(cLine,nOrder,xAlias) // A variável cLine deverá ser passada por referência pois o
local nLast := nArea // retorno da função é o valor lógico resultante da leitura.
local nSLen // Nota: xAlias poderá conter o nome ou o número da área que
local nIPtr // será acessada.
local lRet //
local i //
default xAlias to nArea
*
if (lRet := txSetArea(xAlias))
default nOrder to _FCB_FOCUS
*
if (lRet := (nOrder > 0 .and. nOrder <= _FCB_LINS))
i := (nOrder-1)*_kPOINTER_LEN+1
nIPtr := AlfaSE2Dec(SubStr(_FCB_POINTERS,i ,_kPOINTER_LEN))
nSLen := AlfaSE2Dec(SubStr(_FCB_POINTERS,i+_kPOINTER_LEN,_kPOINTER_LEN))-nIPtr - if(nOrder=_FCB_LINS,0,2)
cLine := Space(nSLen)
*
if nSLen > 0
FSeek(_FCB_FILEHAND,nIPtr,0)
if (lRet := (nSLen := FRead(_FCB_FILEHAND,@cLine,nSLen)) > 0)
cLine := Left(cLine,nSLen)
end
end
cLine := Eval(_FCB_PREGETLIN,cLine)
*
if lRet
_FCB_FOCUS := nOrder+1
end
end
*
if !lRet
txError(_kTXTSYS_READERROR)
end
end
nArea := nLast
return lRet
//**************************************************************************************************
function txLines(xAlias)
local nLast := nArea
local nLins := -1
default xAlias to nArea
*
if txSetArea(xAlias)
nLins := _FCB_LINS
end
nArea := nLast
return nLins
//**************************************************************************************************
function txMaxCol(xAlias)
local nLast := nArea
local nCols := -1
default xAlias to nArea
*
if txSetArea(xAlias)
nCols := _FCB_COLS
end
nArea := nLast
return nCols
//**************************************************************************************************
function txNextMark(nInc,nStart,xAlias)
local nLast := nArea
local nLin := 0
local i
default xAlias to nArea
default nStart to 1
*
if txSetArea(xAlias) .and. Len(_FCB_MARKS) > 0
i := if(nInc>0, 1, Len(_FCB_MARKS))
while TRUE
if if(nInc>0, _FCB_MARKS[i] >= nStart, _FCB_MARKS[i] <= nStart)
nLin := _FCB_MARKS[i]
exit
end
i += if(nInc>0, 1, -1)
if i=if(nInc>0, Len(_FCB_MARKS)+1, 0)
exit
end
end
end
nArea := nLast
return nLin
//**************************************************************************************************
function txOpen(cFName ,; // path+nome do arquivo
cAlias ,; // nome pelo qual esta área de dados poderá ser selecionada futuramente
lCreate ,; // se verdadeiro e o arquivo não existir, será criado (inclusive o path)
nMode ,; // modo de acesso
bPGetLin,; // bloco de código de preparação de leitura de uma linha
bPSetLin ; // bloco de código de preparação de escrita de uma linha
)
local aDir := Directory(cFName)
local nHnd
local lRet
*
default cAlias to cFName
default nMode to _kTXTSYS_ACCESS_READWRITE
default lCreate to FALSE
default bPGetLin to {|c|c}
default bPSetLin to {|c|c}
*
txError()
if GetIndex(cAlias) != 0
txError(_kTXTSYS_ALREADYOPEN)
return FALSE
end
*
if (lRet := Len(aDir) > 0)
if (lRet := (nHnd := FOpen(cFName,nMode)) > 0)
AAdd(aFCB,Array(_kFCB_SIZE))
nArea := Len(aFCB)
_FCB_ALIAS := cAlias
_FCB_FILENAME := cFName
_FCB_FILEHAND := nHnd
_FCB_FILESIZE := aDir[1][2]
_FCB_PREGETLIN := bPGetLin
_FCB_PRESETLIN := bPSetLin
*
BuildIndex()
GarbagColl()
else
txError(_kTXTSYS_OPENERROR)
end
else
if lCreate
if !SeekFPath(FilePath(cFName))
MakeFPath(FilePath(cFName))
end
if (lRet := (nHnd := FCreate(cFName,nMode)) > 0)
AAdd(aFCB,Array(_kFCB_SIZE))
nArea := Len(aFCB)
_FCB_ALIAS := cAlias
_FCB_FILENAME := cFName
_FCB_FILEHAND := nHnd
_FCB_FILESIZE := 0
_FCB_PREGETLIN := bPGetLin
_FCB_PRESETLIN := bPSetLin
else
txError(_kTXTSYS_CREATEERROR)
end
else
txError(_kTXTSYS_FILENOTFOUND)
end
end
return lRet
//**************************************************************************************************
function txSetArea(xAlias) // xAlias poderá conter o número da área ou seu nome
local lRet //
local i //
*
txError()
if ValType(xAlias) = "N"
if (lRet := xAlias > 0 .and. xAlias <= Len(aFCB))
nArea := xAlias
else
txError(_kTXTSYS_SELECTERROR)
end
else
if (lRet := xAlias != VOID .and. (i := GetIndex(xAlias)) > 0)
nArea := i
else
txError(_kTXTSYS_ALIASNOTFOUND)
end
end
return lRet
//**************************************************************************************************
function txSize(xAlias)
local nLast := nArea
local nSize := -1
local lRet
default xAlias to nArea
*
if (lRet := txSetArea(xAlias))
nSize := _FCB_FILESIZE
end
nArea := nLast
return nSizeCódigo: Selecionar todos
function LerTxtTst()
if !txOpen("arquivo.txt","ALIAS",_kTXTSYS_ACCESS_READ) = _kTXTSYS_NOERROR
// Um erro qualquer
return .F.
end
*
cLine := ""
for i := 1 to txLines()
txGetLine(@cLine,i)
//
// cLine contém a linha, sem o par CR/LF
// Processe-a do jeito que precisar.
next
txClose()
return (i>1) // se .F., o arquivo estava vazio.
