Rotina de Extenso

Aqui você poderá oferecer suas Contribuições, Dicas e Tutoriais (Texto ou Vídeo) que sejam de interesse de todos.

Moderador: Moderadores

Avatar do usuário
JoséQuintas
Administrador
Administrador
Mensagens: 20267
Registrado em: 26 Fev 2007 11:59
Localização: São Paulo-SP

Rotina de Extenso

Mensagem por JoséQuintas »

Refiz minha rotina de extenso de valor, mas ficou interessante.... parece mais .... humana
A parte do idioma em português é que complica a rotina mas tudo bem.

Como é que a gente faz o extenso de um valor?
primeira parte com a palavra reais, e segunda parte centavos
N reais e N centavos

Código: Selecionar todos

FUNCTION ze_ExtensoDinheiro( nValor )

   LOCAL cTxt := "", cStrValor, nInteiro, nDecimal

   nValor    := Abs( nValor )
   cStrValor := Str( nValor, 18, 2 )
   nInteiro  := Val( Substr( cStrValor, 1, At( ".", cStrValor ) - 1 ) )
   nDecimal  := Val( Substr( cStrValor, At( ".", cStrValor ) + 1 ) )
   IF nInteiro != 0 .OR. nDecimal == 0
      cTxt += ze_ExtensoNumero( nInteiro ) + " " + iif( nInteiro == 1, "REAL", "REAIS" )
   ENDIF
   IF nDecimal != 0
      IF nInteiro != 0
         cTxt += " E "
      ENDIF
      cTxt += ze_ExtensoNumero( nDecimal ) + " " + iif( nDecimal == 1, "CENTAVO", "CENTAVOS" )
   ENDIF

   RETURN cTxt
E depois?
A gente separa em blocos de três, e acrescenta bilhão, milhão, mil, etc.
N bilhões, N milhões, N mil e N
O detalhe é que coloquei na rotina pra fazer de trás pra frente... vai fazendo até acabar.
E recursividade, sem FOR/NEXT.

Código: Selecionar todos

STATIC FUNCTION ze_ExtensoNumero( nValor, nGrupo )

   LOCAL cTxt := "", cStrValor, nCentena, nResto, cTxtGrupo := "", lNegativo
   LOCAL aList := { "", "MIL", "MILHAO", "BILHAO", "TRILHAO", "QUATRILHAO", ;
      "QUINTILHAO", "SEPTILHAO", "OCTILHAO", "NONILHAO", "DECILHAO" }

   hb_Default( @nGrupo, 1 )
   lNegativo := ( nValor < 0 )
   nValor    := Abs( nValor )
   cStrValor := StrZero( nValor, 16 )
   nCentena  := Val( Right( cStrValor, 3 ) )
   nResto    := Val( Substr( cStrValor, 1, Len( cStrValor ) - 3 ) )
   IF nCentena != 0
      IF nCentena > 0
         cTxtGrupo := aList[ nGrupo ]
         IF nCentena > 1
            cTxtGrupo := StrTran( cTxtGrupo, "LHAO", "LHOES" )
         ENDIF
      ENDIF
      cTxt := ze_ExtensoCentena( nCentena ) + " " + cTxtGrupo
   ENDIF
   IF nResto != 0 .AND. nGrupo < Len( aList )
      cTxt := ze_ExtensoNumero( nResto, nGrupo + 1 ) + " E " + cTxt
   ENDIF
   IF nGrupo == 1
      IF nValor == 0
         cTxt := "ZERO"
      ENDIF
      cTxt := iif( lNegativo, "*NEGATIVO* ", "" ) + AllTrim( cTxt )
   ENDIF

   RETURN cTxt
Aí vém o grupo de 3 números... 999
Como a gente faz? a gente vê 100, 200, pode ser CEM ou CENTO, ou duzentos, trezentos, ... e alguma coisa
A rotina pega esse primeiro número, e se o que sobra for diferente de zero, chama a de 2 números... 99
exemplo... duzentos e N, ela preenche o duzentos e chama a rotina de dezena pra preencher o N

Código: Selecionar todos

STATIC FUNCTION ze_ExtensoCentena( nValor )

   LOCAL aList := { "CENTO", "DUZENTOS", "TREZENTOS", "QUATROCENTOS", ;
      "QUINHENTOS", "SEISCENTOS", "SETECENTOS", "OITOCENTOS", ;
      "NOVECENTOS" }
   LOCAL nCentena, nDezena, cTxt := ""

   nCentena := Int( nValor / 100 )
   nDezena  := Mod( nValor, 100 )
   IF nValor > 0
      IF nCentena == 1 .AND. nDezena == 0
         cTxt += "CEM"
      ELSE
         IF nCentena != 0
            cTxt += aList[ nCentena ]
         ENDIF
         IF nDezena != 0
            IF nCentena != 0
               cTxt += " E "
            ENDIF
            cTxt += ze_ExtensoDezena( nDezena )
         ENDIF
      ENDIF
   ENDIF

   RETURN cTxt
Aí vém a dezena... se for até 19 tem descrição própria, senão é vinte e n, trinta e N, etc.
Ela preenche a primeira parte e chama a rotina de unidade para o N

Código: Selecionar todos

STATIC FUNCTION ze_ExtensoDezena( nValor )

   LOCAL aList := { "DEZ", "VINTE", "TRINTA", "QUARENTA", "CINQUENTA", "SESSENTA", ;
      "SETENTA", "OITENTA", "NOVENTA" }
   LOCAL cTxt := "", nDezena, nUnidade

   IF nValor > 0
      nDezena := Int( nValor / 10 )
      nUnidade := Mod( nValor, 10 )
      IF nValor < 20
         cTxt += ze_ExtensoUnidade( nValor )
      ELSE
         cTxt += aList[ nDezena ]
         IF nUnidade != 0
            cTxt += " E " + ze_ExtensoUnidade( nUnidade )
         ENDIF
      ENDIF
   ENDIF

   RETURN cTxt
E finalmente a unidade.

Código: Selecionar todos

STATIC FUNCTION ze_ExtensoUnidade( nValor )

   LOCAL aList := { "UM", "DOIS", "TRES", "QUATRO", "CINCO", "SEIS", ;
      "SETE", "OITO", "NOVE", "DEZ", "ONZE", "DOZE", "TREZE", ;
      "QUATORZE", "QUINZE", "DEZESSEIS", "DEZESSETE", "DEZOITO", ;
      "DEZENOVE" }

   RETURN aList[ nValor ]
O que achei interessante é que parece o jeito "humano" de fazer.
Foi mais como ensinar o jeito humano ao computador, do que traduzir o jeito humano para o jeito do computador.

E é nisso que muito programador apanha.
O que inventam pra facilitar programação é justamente pra trazer esse jeito humano para o fonte.
Só que o programador fica pensando em computador, e não em humanos.

Achei legal o resultado. A parte mais chata, ou com mais código fonte, é mesmo acertar a parte de português.

milhão ou milhões? bom... se maior que 1 é milhões.
olhem lá o fonte, tá igual o texto... se maior que 1 é ..lhões.

Código: Selecionar todos

         IF nCentena > 1
            cTxtGrupo := StrTran( cTxtGrupo, "LHAO", "LHOES" )
         ENDIF
José M. C. Quintas
Harbour 3.2, mingw, gtwvg mt, fivewin 25.04, multithread, dbfcdx, MySQL, ADOClass, PDFClass, SefazClass, (hwgui mt), (hmg3), (hmg extended), (oohg), PNotepad, ASP, stored procedure, stored function, Linux (Flagship/harbour 3.2)
"The world is full of kings and queens, who blind our eyes and steal our dreams Its Heaven and Hell"

https://github.com/JoseQuintas/
Avatar do usuário
JoséQuintas
Administrador
Administrador
Mensagens: 20267
Registrado em: 26 Fev 2007 11:59
Localização: São Paulo-SP

Rotina de Extenso

Mensagem por JoséQuintas »

E a parte legal de separar os números, é que dá pra usar numa rotina de extenso de data, por exemplo.

Código: Selecionar todos

   ELSEIF ValType( xValue ) == "D"
      IF ! xFull
         cTxt := StrZero( Day( xValue ), 2 ) + " DE " + ze_ExtensoMes( xValue ) + " DE " + StrZero( Year( xValue ), 4 )
      ELSE
         cTxt := ze_ExtensoNumero( Day( xValue ) )
         cTxt += " DE " + ze_ExtensoMes( xValue ) + " DE "
         cTxt += ze_ExtensoNumero( Year( xValue ) )
         DO WHILE Space(2) $ cTxt
            cTxt := StrTran( cTxt, Space(2), Space(1) )
         ENDDO
      ENDIF
   ENDIF
José M. C. Quintas
Harbour 3.2, mingw, gtwvg mt, fivewin 25.04, multithread, dbfcdx, MySQL, ADOClass, PDFClass, SefazClass, (hwgui mt), (hmg3), (hmg extended), (oohg), PNotepad, ASP, stored procedure, stored function, Linux (Flagship/harbour 3.2)
"The world is full of kings and queens, who blind our eyes and steal our dreams Its Heaven and Hell"

https://github.com/JoseQuintas/
Responder