Função que converte o valor do Soma1 em AdvPL

Olá pessoal…

Hoje vou mostrar uma rotina desenvolvida para converter valores gerados pelo Soma1 (como por exemplo, após o 99, vir o 9A, depois o 9B, 9C, 9D até chegar no ZZ).


Foram desenvolvidas duas rotinas, a primeira converte 100% dos casos, porém é bem mais demorada, pois é feito um While, percorrendo até chegar no valor do soma1.

A segunda, pelos testes realizados, converte 100% quando o valor tem 1 posição ou duas (ou seja, no tamanho máximo de “ZZ”), a partir de 3 posições, o fator fica impreciso no valor de AB0, pois não consegui chegar em um fator de conversão exato.

AdvPL

AdvPL

Abaixo o código desenvolvido com as duas funções.

//Bibliotecas
#Include "Protheus.ch"

/*/{Protheus.doc} zCnvSoma1
Função de conversão do Soma1
@type function
@author Atilio
@since 03/08/2016
@version 1.0
	@param cValor, character, Valor do Soma1
	@return nValConv, Valor convertido
	@example
	u_zCnvSoma1("ZZZ")
/*/

User Function zCnvSoma1(cValor)
	Local aArea    := GetArea()
	Local nValConv := 0
	Local cAtual   := ""
	Default cValor := "0"
	
	//Definindo o atual como 0
	cAtual := StrZero(0, Len(cValor))
	cAtual := StrTran(cAtual, '0', '9')
	
	//Enquanto o valor atual for diferente do parâmetro
	While cAtual != cValor
		nValConv++
		cAtual := Soma1(cAtual)
	EndDo
	
	RestArea(aArea)
Return nValConv

/*/{Protheus.doc} zCnvTipo2
Função para conversão do Soma1 (sem utilizar o While)
@type function
@author Atilio
@since 03/08/2016
@version 1.0
	@param cNumero, character, Valor do Soma1
	@return nValor, Valor Convertido
	@example
	u_zCnvSoma1("ZZ")
	@obs Essa função não foi finalizada 100%!
	Não foi encontrado um fator exato de conversão, em testes realizados, com valores de tamanho 1 e 2, a função funciona
	perfeitamente (por exemplo, A0, ZZ, Z, M9, etc), porém para valores de tamanho 3 para cima não.
	Para valores de tamanho 1, a rotina funciona
	Para valores de tamanho 2, a rotina funciona
	Para valores de tamanho 3, a inconsistência começa partir do AB0  (após o AAZ)  - 2357  registros
	Para valores de tamanho 4, a inconsistência começa partir do 9AB0 (após o 9AAZ) - 11356 registros
/*/

User Function zCnvTipo2(cNumero)
	Local aArea     := GetArea()
	Local nValor    := 0
	Local lSoNumero := .T.
	Local nAtual    := 0
	Local cAscii    := ""
	Local nPosIni   := 0
	Local cCaract   := ""
	Local nValAux   := 0
	Local cZeros    := ""
	Local cAscii    := ""
	cNumero := Upper(cNumero)
	
	//Percorre os valores
	For nAtual := 1 To Len(cNumero)
		cCaract := SubStr(cNumero, nAtual, 1)
		
		//Se tiver alguma letra no numero
		If cCaract $ "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
			If nPosIni == 0
				nPosIni := nAtual
			EndIf
			lSoNumero := .F.
			Exit
		EndIf
	Next
	
	//Se tiver somente numero, converte com Val
	If lSoNumero
		nValor := Val(cNumero)
		
	Else
		nValor := 0
		
		//Percorre os valores
		For nAtual := 1 To Len(cNumero)
			cCaract := SubStr(cNumero, nAtual, 1)
			cZeros  := Replicate("0", Len(cNumero)-nAtual)
			
			//Se tiver alguma letra no numero
			If cCaract $ "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
				cAscii := cValToChar(Asc(cCaract) - 64 + 9)
				
				//Se for a partir da segunda posição e não for a última
				If nAtual > nPosIni .And. nAtual != Len(cNumero)
					nValAux := Val(cAscii + cZeros) + Iif(nAtual != Len(cNumero), 26 * (Asc(cCaract) - 64), 0)
					nValAux *= Val(cAscii)
					nValAux += (26 + Val(cAscii))
					nValor += nValAux
					
				Else
					nValor += Val(cAscii + cZeros) + Iif(nAtual != Len(cNumero), 26 * (Asc(cCaract) - 64), 0)
				EndIf
			
			//Se for somente números
			Else
				//Se for a partir da segunda posição e não for a última
				If nAtual > nPosIni .And. nAtual != Len(cNumero)
					nValor += Val(cCaract + cZeros) + (36 * 26) + (26*Val(cCaract))
				Else
					nValor += Val(cCaract + cZeros)
				EndIf
			EndIf
		Next		
	EndIf
	
	RestArea(aArea)
Return nValor

Bom pessoal, por hoje é só.
Abraços e até a próxima.

Dan Atilio (Daniel Atilio)
Especialista em Engenharia de Software pela FIB. Entusiasta de soluções Open Source. E blogueiro nas horas vagas.

6 Responses

  1. Gustavo disse:

    Existe a função DecodSoma1 que faz isso e é muito rápido.

  2. José Murilo disse:

    Danilo, segue uma contribuição, pois já me servi várias vezes dos seus exemplos.
    Não achei documentação sobre o DecodSoma1, mas ele não serviu no momento, pois não funcionou como eu esperava, ao passar uma string com 2 dígitos, não somou como no Soma1, talvez tenha algum parâmetro. Bem o Soma1, até que atenderia, mas não gostei da sequência dele após o 99, é usado de 9A a 9Z, porque não usar também para 00 a 0Z, 10 a 1Z…, foi o que fiz com as funções abaixo, assim somando os dígitos 0 a 9 e A a Z, temos 36, usando 2 dígitos, 36 X 36 = 1296 combinações, considerando o ’00’:

    cSquenc := ’00’
    cSquenc := fXSoma1(cSequenc)

    //#############################################################################
    // Função: fXSoma1 Autor: José Murilo: 13/10/2020
    // Descrição: Fornecer sequência alfanumérica com os dígitos 0 a 9 e A a Z
    //—————————————————————————–
    // ASCII 048 a 057 números 0 – 9
    // ASCII 065 a 090 letras A – Z maiúsculas
    // A vantagem sobre o Soma1 é que podemos ter até 1296 combinações contra 1035,
    // usando apenas 2 dígitos. 00 a 0Z, 10 a 1Z, 20 a 2Z, 30 a 3Z…
    //#############################################################################
    static function fXSoma1(pcNum)
    Local cRet as character
    Local aNum as array

    aNum := {substr(pcNum,1,1), substr(pcNum,2,1)}

    if aNum[2] == ‘Z’
    aNum[1] := fXPrxDig(aNum[1])
    aNum[2] := fXPrxDig(aNum[2])
    else
    aNum[2] := fXPrxDig(aNum[2])
    endif

    cRet := aNum[1] + aNum[2]

    return(cRet)

    //#############################################################################
    // Função…: fXPrxDig Autor: José Murilo: 13/10/2020
    // Descrição: Retorna o próximo dígito da sequência alfanumérica com os dígitos.
    // 0 a 9 e A a Z
    //—————————————————————————–
    // ASCII 048 a 057 números 0 – 9
    // ASCII 065 a 090 letras A – Z maiúsculas
    //#############################################################################
    static function fXPrxDig(pcDig)
    Local cRet as character

    if pcDig == ‘9’
    cRet := ‘A’
    elseif pcDig == ‘Z’
    cRet := ‘0’
    else
    cRet := chr(asc(pcDig)+1)
    endif

    return(cRet)

    Abraços, obrigado por compartilhar e parabéns pelo trabalho no blog.

  3. dwsy disse:

    “Futucando” alguns fontes antigos da LIB, encontrei a função RetAsc() que é usada nas rotinas que convertem esse tipo de campo nos grids:

    Sintaxe: RetAsc(cString, nTamanho, lVolta) onde lVolta == .T. faz o processo contrário (numero para letra)

Deixe uma resposta