BlackTDN Search

domingo, 1 de maio de 2011

Protheus :: Alternativa às Funções do Tipo FT_F*

Outro dia precisei ler um arquivo, através das funções FT_F* e, surpreso, descobri que ela possui limitações: Função: FT_FReadLn (Lê e retorna uma linha de texto do arquivo aberto pela função FT_FUse(). As linhas do texto, são delimitadas pela sequência de caracteres CRLF (chr(13)+chr(10)) ou apenas LF (chr(10)), e o tamanho máximo de cada linha é 1022 bytes.).

O arquivo que estava tentando ler superava, em muito, a quantidade de caracteres suportado pela FT_FReadLn: 2668 no total ( quase 3 vezes mais ).

Para a solução do problema, desenvolvi a classe fT com as mesmas semelhanças as funções FT_F* e sem as limitações impostas por esta.

#INCLUDE "PROTHEUS.CH"
#INCLUDE "TRYEXCEPTION.CH"
#INCLUDE "FILEIO.CH"
/*/
 CLASS:  fT
 Autor:  Marinaldo de Jesus
 Data:  01/05/2011
 Descricao: Alternativa aas funcoes tipo FT_F* devido as limitacoes apontadas em (http://tdn.totvs.com.br/kbm#9734)
 Sintaxe: ft():New() : Objeto do Tipo fT
/*/
CLASS fT FROM LongClassName

 DATA aLines
 
 DATA cCRLF
 DATA cFile
 DATA cLine

 DATA cClassName

 DATA nRecno
 DATA nfHandle
 DATA nFileSize
 DATA nLastRecno
 DATA nBufferSize

 METHOD New()  CONSTRUCTOR
 METHOD ClassName()

 METHOD ft_fUse( cFile )
 METHOD ft_fOpen( cFile )
 METHOD ft_fClose()
 
 METHOD ft_fAlias()
 
 METHOD ft_fExists( cFile )
 
 METHOD ft_fRecno()
 METHOD ft_fSkip( nSkipper )
 METHOD ft_fGoTo( nGoTo )
 METHOD ft_fGoTop()
 METHOD ft_fGoBottom()
 METHOD ft_fLastRec()
 METHOD ft_fRecCount()

 METHOD ft_fEof()
 METHOD ft_fBof()

 METHOD ft_fReadLn()
 METHOD ft_fReadLine()
 
 METHOD ft_fError( cError )

 METHOD ft_fSetCRLF( cCRLF )
 METHOD ft_fSetBufferSize( nBufferSize )

END CLASS

User Function ft()
Return( NIL )

/*/
 METHOD:  New
 Autor:  Marinaldo de Jesus
 Data:  01/05/2011
 Descricao: CONSTRUCTOR
 Sintaxe: ft():New() : Object do Tipo fT    
/*/
METHOD New() CLASS fT

 Self:aLines   := Array(0) 

 Self:cFile   := ""
 Self:cLine   := ""

 Self:cClassName  := "FT"

 Self:nRecno   := 0
 Self:nLastRecno  := 0
 Self:nfHandle  := -1
 Self:nFileSize  := 0

 Self:ft_fSetCRLF()
 Self:ft_fSetBufferSize()

Return( Self )

/*/
 METHOD:  ClassName
 Autor:  Marinaldo de Jesus
 Data:  01/05/2011
 Descricao: Retornar o Nome da Classe
 Sintaxe: ft():ClassName() : Retorna o Nome da Classe
/*/
METHOD ClassName() CLASS fT
Return( Self:cClassName )

/*/
 METHOD:  ft_fUse
 Autor:  Marinaldo de Jesus
 Data:  01/05/2011
 Descricao: Abrir o Arquivo Passado como Parametro
 Sintaxe: ft():ft_fUse( cFile ) : nfHandle ( nfHandle > 0 True, False)
/*/
METHOD ft_fUse( cFile ) CLASS fT

 TRYEXCEPTION

  IF !( Self:ft_fExists( cFile ) )
   BREAK
  EndIF

  Self:ft_fOpen( cFile )
 
 CATCHEXCEPTION

  Self:ft_fClose()

 ENDEXCEPTION

Return( Self:nfHandle )

/*/
 METHOD:  ft_fOpen
 Autor:  Marinaldo de Jesus
 Data:  01/05/2011
 Descricao: Abrir o Arquivo Passado como Parametro
 Sintaxe: ft():ft_fOpen( cFile ) : nfHandle ( nfHandle > 0 True, False)
/*/
METHOD ft_fOpen( cFile ) CLASS fT

 TRYEXCEPTION

  IF !( Self:ft_fExists( cFile ) )
   BREAK
  EndIF

  Self:cFile  := cFile
  Self:nfHandle := fOpen( Self:cFile , FO_READ )
  
  IF ( Self:nfHandle <= 0 )
   BREAK
  EndIF
  
  Self:nFileSize := fSeek( Self:nfHandle , 0 , FS_END )

  fSeek( Self:nfHandle , 0 , FS_SET )

  Self:nFileSize := ReadFile( @Self:aLines , @Self:nfHandle , @Self:nBufferSize , @Self:nFileSize , @Self:cCRLF )

  Self:ft_fGoTop()

 CATCHEXCEPTION
 
  Self:nfHandle := -1
 
 ENDEXCEPTION

Return( Self:nfHandle )

/*/
 Funcao:  ReadFile
 Autor:  Marinaldo de Jesus
 Data:  01/05/2011
 Descricao: Percorre o Arquivo a ser lido e alimento o Array aLines
 Sintaxe: ReadFile( aLines , nfHandle , nBufferSize , nFileSize , cCRLF ) : nLines Read
/*/
Static Function ReadFile( aLines , nfHandle , nBufferSize , nFileSize , cCRLF )
    
 Local cLine   := ""
 Local cBuffer  := ""

 Local nLines  := 0
 Local nAtPlus  := ( Len( cCRLF ) -1 )
 Local nBytesRead := 0

 While ( nBytesRead <= nFileSize )
  cBuffer   += fReadStr( @nfHandle , @nBufferSize )
  nBytesRead  += nBufferSize
  While ( cCRLF $ cBuffer )
   ++nLines
   cLine   := SubStr( cBuffer , 1 , ( AT( cCRLF , cBuffer ) + nAtPlus ) )
   cBuffer  := SubStr( cBuffer , Len( cLine ) + 1 )
   cLine  := StrTran( cLine , cCRLF , "" )
   aAdd( aLines , cLine )
   cLine  := ""
  End While
 End While

 IF !Empty( cBuffer )
  ++nLines
  aAdd( aLines , cBuffer )
  cBuffer := ""
 EndIF

Return( nLines )

/*/
 METHOD:  ft_fClose
 Autor:  Marinaldo de Jesus
 Data:  01/05/2011
 Descricao: Fechar o Arquivo aberto pela ft_fOpen ou ft_fUse
 Sintaxe: ft():ft_fClose() : NIL
/*/
METHOD ft_fClose() CLASS fT

 IF ( Self:nfHandle > 0 )
  fClose( Self:nfHandle )
 EndIF

 aSize( Self:aLines , 0 )

 Self:cFile   := ""
 Self:cLine   := ""

 Self:nRecno   := 0
 Self:nfHandle  := -1
 Self:nFileSize  := 0
 Self:nLastRecno  := 0

Return( NIL )

/*/
 METHOD:  ft_fAlias
 Autor:  Marinaldo de Jesus
 Data:  01/05/2011
 Descricao: Retornar o Nome do Arquivo Atualmente Aberto
 Sintaxe: ft():ft_fAlias() : cFile
/*/
METHOD ft_fAlias() CLASS fT
Return( Self:cFile )

/*/
 METHOD:  ft_fExists
 Autor:  Marinaldo de Jesus
 Data:  01/05/2011
 Descricao: Verifica se o Arquivo Existe
 Sintaxe: ft():ft_fExists( cFile ) : lExists
/*/
METHOD ft_fExists( cFile ) CLASS fT

 Local lExists

 TRYEXCEPTION

  IF Empty( cFile )
   BREAK
  EndIF

  lExists := File( cFile )

 CATCHEXCEPTION
 
  lExists := .F.

 ENDEXCEPTION

Return( lExists )

/*/
 METHOD:  ft_fRecno
 Autor:  Marinaldo de Jesus
 Data:  01/05/2011
 Descricao: Retorna o Recno Atual
 Sintaxe: ft():ft_fRecno() : nRecno
/*/
METHOD ft_fRecno() CLASS fT
Return( Self:nRecno )

/*/
 METHOD:  ft_fSkip
 Autor:  Marinaldo de Jesus
 Data:  01/05/2011
 Descricao: Salta n Posicoes 
 Sintaxe: ft():ft_fSkip( nSkipper ) : nRecno
/*/
METHOD ft_fSkip( nSkipper ) CLASS fT

 DEFAULT nSkipper := 1

 Self:nRecno += nSkipper

Return( Self:nRecno )

/*/
 METHOD:  ft_fGoTo
 Autor:  Marinaldo de Jesus
 Data:  01/05/2011
 Descricao: Salta para o Registro informando em nGoto
 Sintaxe: ft():ft_fGoTo( nGoTo ) : nRecno
/*/
METHOD ft_fGoTo( nGoTo ) CLASS fT

 Self:nRecno := nGoTo

Return( Self:nRecno )

/*/
 METHOD:  ft_fGoTop
 Autor:  Marinaldo de Jesus
 Data:  01/05/2011
 Descricao: Salta para o Inicio do Arquivo
 Sintaxe: ft():ft_fGoTo( nGoTo ) : nRecno
/*/
METHOD ft_fGoTop() CLASS fT
Return( Self:ft_fGoTo( 1 ) )

/*/
 METHOD:  ft_fGoBottom
 Autor:  Marinaldo de Jesus
 Data:  01/05/2011
 Descricao: Salta para o Final do Arquivo
 Sintaxe: ft():ft_fGoBottom() : nRecno
/*/
METHOD ft_fGoBottom() CLASS fT
Return( Self:ft_fGoTo( Self:nFileSize ) )

/*/
 METHOD:  ft_fLastRec
 Autor:  Marinaldo de Jesus
 Data:  01/05/2011
 Descricao: Retorna o Numero de Registro do Arquivo
 Sintaxe: ft():ft_fLastRec() : nRecCount
/*/
METHOD ft_fLastRec() CLASS fT
Return( Self:nFileSize )

/*/
 METHOD:  ft_fRecCount
 Autor:  Marinaldo de Jesus
 Data:  01/05/2011
 Descricao: Retorna o Numero de Registro do Arquivo
 Sintaxe: ft():ft_fRecCount() : nRecCount
/*/
METHOD ft_fRecCount() CLASS fT
Return( Self:nFileSize )

/*/
 METHOD:  ft_fEof
 Autor:  Marinaldo de Jesus
 Data:  01/05/2011
 Descricao: Verifica se Atingiu o Final do Arquivo
 Sintaxe: ft():ft_fEof() : lEof
/*/
METHOD ft_fEof() CLASS fT
Return( Self:nRecno > Self:nFileSize )

/*/
 METHOD:  ft_fBof
 Autor:  Marinaldo de Jesus
 Data:  01/05/2011
 Descricao: Verifica se Atingiu o Inicio do Arquivo
 Sintaxe: ft():ft_fBof() : lBof
/*/
METHOD ft_fBof() CLASS fT
Return( Self:nRecno < 1 )

/*/
 METHOD:  ft_fReadLine
 Autor:  Marinaldo de Jesus
 Data:  01/05/2011
 Descricao: Le a Linha do Registro Atualmente Posicionado
 Sintaxe: ft():ft_fReadLine() : cLine
/*/
METHOD ft_fReadLine() CLASS fT

 TRYEXCEPTION

  Self:nLastRecno := Self:nRecno
  Self:cLine  := Self:aLines[ Self:nRecno ]

 CATCHEXCEPTION

  Self:cLine := ""

 ENDEXCEPTION

Return( Self:cLine )

/*/
 METHOD:  ft_fReadLn
 Autor:  Marinaldo de Jesus
 Data:  01/05/2011
 Descricao: Le a Linha do Registro Atualmente Posicionado
 Sintaxe: ft():ft_fReadLn() : cLine
/*/
METHOD ft_fReadLn() CLASS fT
Return( Self:ft_fReadLine() )

/*/
 METHOD:  ft_fError
 Autor:  Marinaldo de Jesus
 Data:  01/05/2011
 Descricao: Retorna o Ultimo erro ocorrido
 Sintaxe: ft():ft_fError( @cError ) : nDosError
/*/
METHOD ft_fError( cError ) CLASS fT
 cError := CaptureError()
Return( fError() )

/*/
 METHOD:  ft_fSetBufferSize
 Autor:  Marinaldo de Jesus
 Data:  01/05/2011
 Descricao: Redefine nBufferSize
 Sintaxe: ft():ft_fSetBufferSize( nBufferSize ) : nLastBufferSize
/*/
METHOD ft_fSetBufferSize( nBufferSize ) CLASS fT

 Local nLastBufferSize := Self:nBufferSize

 DEFAULT nBufferSize := 1024
 
 Self:nBufferSize := nBufferSize
 Self:nBufferSize := Max( Self:nBufferSize , 1 )

Return( nLastBufferSize )

/*/
 METHOD:  ft_fSetCRLF
 Autor:  Marinaldo de Jesus
 Data:  01/05/2011
 Descricao: Redefine cCRLF
 Sintaxe: ft():ft_fSetCRLF( cCRLF ) : nLastCRLF
/*/
METHOD ft_fSetCRLF( cCRLF ) CLASS fT

 Local cLastCRLF := Self:cCRLF
 
 DEFAULT cCRLF := CRLF
 
 Self:cCRLF  := cCRLF

Return( cLastCRLF )

Abaixo um exemplo de uso

#INCLUDE "PROTHEUS.CH"
/*/
 Funcao:  FTFSample
 Autor:  Marinaldo de Jesus
 Data:  01/05/2011
 Descricao: Exemplo de Uso da Classe fT
/*/
User Function FTFSample()

 Local aCab
 Local aDet

 Local cFile   := "fTfSample.csv" //Deve estar em \system\

 aFile     := FileToArr( @cFile )
 aCab    := aFile[1]
 aDet     := aFile[2]

Return( NIL )

/*/
 Funcao:  FileToArr
 Autor:  Marinaldo de Jesus
 Data:  01/05/2011
 Descricao: Exemplo de Uso da Classe fT
/*/
Static Function FileToArr( cFile )

 Local aCab   := {}
 Local aDet   := {}

 Local cLine   := ""
 Local cToken  := Chr(255)

 Local oFT   := fT():New()

 BEGIN SEQUENCE

  IF ( oFT:ft_fUse( cFile ) <= 0 )
   BREAK
  EndIF

  While !( oFT:ft_fEof() )
   IncProc()
   cLine   := oFT:ft_fReadLn()
   ConOut( cLine )
   cLine  := StrTran( cLine , '""' , '" "' )    //Carrego um espaço em branco
   cLine  := StrTran( cLine , '","' , cToken ) //Defino o Separador
   cLine  := StrTran( cLine , '"' , "" )   //Retiro as Aspas 
   IF ( oFT:ft_fRecno() == 1 )
    aCab := StrTokArr( cLine , cToken )   //A primeira Linha contem o Cabeçalho dos campos
   Else
    aAdd( aDet , StrTokArr( cLine , cToken ) )  //As demais linhas sao os Detalhes
   EndIF
   cLine  := "" 
   oFT:ft_fSkip()
  End While

  oFT:ft_fUse()

 END SEQUENCE

Return( { aCab , aDet } )

Para baixar o código da classe, do exemplo e dos arquivos usados, clique aqui. []s иαldσ dj

3 comentários:

  1. Gostaria de saber se o arquivo que estou lendo precisa estar dentro do protheus_data?

    ResponderExcluir