Página 1 de 1
ReGex para buscar TAG de XML
Enviado: 14 Nov 2024 10:30
por clodoaldomonteiro
Olá!
No xHarbour tenho a necessidade de pegar todas as TAGs "lan:LancamentoContabilItem" de uma string XML usando a função hb_RegexAll() -> Array.
Código: Selecionar todos
...
cNode := 'lan:LancamentoContabilItem'
cPattern := "<" + cNode + ">([^<]*)</" + cNode + ">"
aLancamentoContabilItem := HB_RegExAll( cPattern, cLancamentoXML )
...
String XML:
Código: Selecionar todos
<lan:LancamentoContabil><!--Obs.DIA: RTPag->001527069326, CAP:416108|414381 -->
<lan:numeroRegiOuLancamento>01027804</lan:numeroRegiOuLancamento>
<lan:dataRegiOuLancamento>2024-01-24</lan:dataRegiOuLancamento>
<lan:tipoLancContabil>1</lan:tipoLancContabil>
<lan:tipoMoviContabil>2</lan:tipoMoviContabil>
<lan:historicoRegiContabil>DIA Cod: 0126798, RTP[LCP 88.193] Pagamento Extra, Cód RTP: 001527-069326, Tipo: 15 - RPPS (Demais)</lan:historicoRegiContabil>
<lan:codigoUnidOrcamentaria>000101</lan:codigoUnidOrcamentaria>
<lan:LancamentoContabilItem><!--CAP:416108 -->
<lan:tipoNatuLancamento>1</lan:tipoNatuLancamento>
<lan:valorLancado>2067.26</lan:valorLancado>
<lan:ContaCorrente>
<cc:DisponibilidadeFinanceira>
<cc:fonteRecurso>
<gen:ioc>1</gen:ioc>
<gen:tipoFontRecurso>500</gen:tipoFontRecurso>
<gen:tipoCompFontRecurso>9999</gen:tipoCompFontRecurso>
</cc:fonteRecurso>
<cc:atributoSupeFinanceiro xsi:nil="true" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"/>
<cc:codigoContContabil>821130200</cc:codigoContContabil>
</cc:DisponibilidadeFinanceira>
</lan:ContaCorrente>
</lan:LancamentoContabilItem>
<lan:LancamentoContabilItem><!--CAP:414381 -->
<lan:tipoNatuLancamento>2</lan:tipoNatuLancamento>
<lan:valorLancado>2067.26</lan:valorLancado>
<lan:ContaCorrente>
<cc:DisponibilidadeFinanceira>
<cc:fonteRecurso>
<gen:ioc>1</gen:ioc>
<gen:tipoFontRecurso>500</gen:tipoFontRecurso>
<gen:tipoCompFontRecurso>9999</gen:tipoCompFontRecurso>
</cc:fonteRecurso>
<cc:atributoSupeFinanceiro xsi:nil="true" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"/>
<cc:codigoContContabil>821140200</cc:codigoContContabil>
</cc:DisponibilidadeFinanceira>
</lan:ContaCorrente>
</lan:LancamentoContabilItem>
</lan:LancamentoContabil>
</lan:LancamentosContabeis>
O erro acontece pq está retornando NIL.
Grato pela ajuda de todos
ReGex para buscar TAG de XML
Enviado: 14 Nov 2024 11:19
por Itamar M. Lins Jr.
Olá!
Acredito que hb_RegExALl() não é para essa finalidade.
Isso é para EXPRESSÕES REGULARES.
No caso vc usa a função do José Quintas, para pegar as tags do XML. É mais fácil.
Saudações,
Itamar M. Lins Jr.
ReGex para buscar TAG de XML
Enviado: 14 Nov 2024 11:48
por JoséQuintas
Pelas funções da sefazclass talvez fique mais tranquilo.
Vai usar basicamente 2 funções.
Como é o XML:
[campo]xxxx[/campo]
o mesmo nome marca início e fim, a diferença é que o fim tem a barra.
Isso se repete por todo o XML, com níveis.
XmlNode() é a que pega um determinado campo
E tem multipleNodeToArray(), que é pra pegar quando tem uma lista de nomes repetidos.
É ir pegando os blocos e separando o que interessa.
Por exemplo
Código: Selecionar todos
cXMLLancamento := XmlNode( cXml, "lan:LancamentoContabil" )
cHistorico := XmlNode( cXmlLancamento, "lan:historicoRegiContabil" )
Também existe função que transforma XML em HASH, é outra alternativa.
ReGex para buscar TAG de XML
Enviado: 16 Nov 2024 11:06
por clodoaldomonteiro
A questão de acionar aqui os amigos, é que ao usar o método :selectSingleNode() em um XML grande (50MB), o processamento ficou muito lento, caiu de 2.000 /s para 200 /s, acho que por nesse método, poder usar XPath.
JoséQuintas escreveu:Pelas funções da sefazclass talvez fique mais tranquilo.
Vai usar basicamente 2 funções.
Como é o XML:
[campo]xxxx[/campo]
o mesmo nome marca início e fim, a diferença é que o fim tem a barra.
Isso se repete por todo o XML, com níveis.
XmlNode() é a que pega um determinado campo
E tem multipleNodeToArray(), que é pra pegar quando tem uma lista de nomes repetidos.
...
Código: Selecionar todos
cXMLLancamento := XmlNode( cXml, "lan:LancamentoContabil" )
cHistorico := XmlNode( cXmlLancamento, "lan:historicoRegiContabil" )
Bom, aí recorri às funções do @Quintas, e já às uso faz tempo, mas na função multipleNodeToArray(), que pega os lançamentos de um Node (String com mais de 3MB) de lançamentos q tem mais 2.000 registros, o processo ficou muito lento, acho que por ser uma varredura numa string gigante.
Itamar M. Lins Jr. escreveu:Olá!
Acredito que hb_RegExALl() não é para essa finalidade.
Isso é para EXPRESSÕES REGULARES.
A função dá sim pra fazer o que falei acima, inclusive testando num serviço web, alguns regex, traz os resultados, mas vi que no Harbour tem alguma limitação com a biblioteca regex ou a string regex não está devida equacionada.
Exemplo:
Código: Selecionar todos
//Retorna em uma array todos os comentários da string XML.
cPattern := "<!--(.*?)-->"
aMatches := hb_REGEXALL( cPattern, cLancamentoXML )
Estou agora testando o método :getElementsByTagName() que á mais simples que :selectSingleNode(), e vendo se fica com a velocidade de processamento que imagino deva ter (vi num post aqui do @Rochinha).
Código: Selecionar todos
...
objXml := TOLEAUTO():New( "Microsoft.XMLDOM" )
objXml:async := .F.
objXml:validateOnParse := .F.
If objXml:load(mArq_) == .F.
MsgError("Erro ao carregar o arquivo XML.")
Break
Endif
nodes := objXml:getElementsByTagName("lan:PrestacaoContas")
nodePrestConta := nodes:item(0)
mCodigoUnidGestora := xmlGetTextNode(nodePrestConta, "aux:codigoUnidGestora")
...
...
//Retorna valor de :Text de um :Item Node.
Function xmlGetTextNode(oXml, cNodeName)
Local oNodes, oNode, r := ''
// Verifica se o XML é válido
IF Empty(oXml)
Return r
ENDIF
// Busca os nós pelo nome
oNodes := oXml:getElementsByTagName(cNodeName)
// Verifica se encontrou ao menos um nó
IF oNodes:length > 0
oNode := oNodes:Item(0)
IF !Empty(oNode)
r := oNode:text
ENDIF
ENDIF
// Retorna o valor ou Empty se não encontrado e garantindo q ainda assim é o Text.
Return r
Grato pela atenção de todos.
ReGex para buscar TAG de XML
Enviado: 16 Nov 2024 11:32
por JoséQuintas
Já que a finalidade é desse XML grandão..... talvez isto na parte de lançamentos
Mesmo que divida em XMLs inválidos, vai separar cada lançamento que continuará podendo ser tratado por XmlNode().
Se o problema de demora for nessa divisão, vai agilizar.
ReGex para buscar TAG de XML
Enviado: 16 Nov 2024 11:58
por clodoaldomonteiro
O primeiro parâmetro de HB_RegExSplit() é uma string regex, já até tentei usar ela, e hb_regexAll() seria melhor mesmo. O problema está em achar a string gerex correta, ou até ver se isso é uma limitação da biblioteca regex do harbour.
HB_RegExSplit()
Parses a string using a regular expression and fills an array.
Syntax
HB_RegExSplit( <cRegEx> , ;
<cString> , ;
[<lCaseSensitive>, ;
[<lNewLine>] , ;
[<nMaxMatches>] ) --> aSplitString
Arguments
<cRegEx>
This is a character string holding the search pattern as a literal regular expression. Alternatively, the return value of HB_RegExComp() can be used.
<cString>
This is the character string being searched for a match.
<lCaseSensitive>
This parameter defaults to .T. (true) so that a case sensitive search is performed. Passing .F. (false) results in a case insensitive search.
<lNewLine>
This parameter defaults to .F. (false).
<nMaxMatches>
This is a numeric value indicating the maximum number of matches to return. The default value is zero which returns all matches. Return
The function returns an array whose elements hold the parsing result. If no match is found, the return value is NIL.
Description
Function HB_RegExSplit() scans a character string for substrings matching a regular expression. All matched substrings are removed while the remaining strings are collected and returned in an array.
ReGex para buscar TAG de XML
Enviado: 16 Nov 2024 13:41
por JoséQuintas
Eu uso pra dividir linhas.
Nunca reparei sobre expressão regular.
ReGex para buscar TAG de XML
Enviado: 18 Nov 2024 14:25
por clodoaldomonteiro
O processo de busca no XML "grandão" acelerou muito com XPath correto:
-> oLancamentoContabilItem := oLancamentoContabel:selectNodes( '//lan:LancamentoContabilItem' )
para
-> oLancamentoContabilItem := oLancamentoContabel:selectNodes( './lan:LancamentoContabilItem' )
No primeiro caso, forçava uma varredora em aproximadamente 49000 Nodes e no segundo, foca somente nos Itens de lançamento do Node Pai, que são 3400, ou seja, "//" é diferente de "./" (estudar sempre é bom) hehe.
O tempo reduziu de 27 segs para 13 segs.
Mas achei muito tempo ainda, pois cada "oItem" de "oLancamento" tem mais ou menos 1K de string XML, e pegando essa string "cItemXML := oIten:XML" e processando ela com a função do @Quintas ou com:
Código: Selecionar todos
hb_RegExAll("<lan:tipoNatuLancamento>(.*?)</lan:tipoNatuLancamento>", cItemXML)
... o tempo cai para 1 segundo, processando os 3400 registros de lançamentos Débito e Crédito.
É uma diferença bem considerável e pode ser que ainda esteja "patinando" no XPath dos métodos XML.
Para ajudar, fiz uma pequena função para deixar o código mais limpo.
Código: Selecionar todos
//Retorna valor do primeiro Row de acordo com o regex
Function ReGexGetValue(cRegex, cString)
Local r := '',aMatch
aMatch := hb_RegExAll(cRegex, cString)
If Len( aMatch ) > 0
r := aMatch[1, 2]
Endif
Return r
Abraços.
ReGex para buscar TAG de XML
Enviado: 18 Nov 2024 16:15
por Itamar M. Lins Jr.
Olá!
Teve uma vez aqui que a solução que encontrei foi, ir diminuindo a Array depois de processado uma linha.
A medida que ia pegando os campos, ia cortando ela com adel...
Saudações,
Itamar M. Lins Jr.
ReGex para buscar TAG de XML
Enviado: 18 Nov 2024 16:50
por JoséQuintas
E como vai trabalhar muito com string, pode pensar em passar por referência.
Isso usa a variável direta, sem ter que criar uma nova.
Isso é válido pra elementos de array, uso muito no dlgauto, mas não pra economizar memória, nem pra deixar mais rápido.
ReGex para buscar TAG de XML
Enviado: 19 Nov 2024 13:35
por clodoaldomonteiro
Itamar M. Lins Jr. escreveu:Olá!
Teve uma vez aqui que a solução que encontrei foi, ir diminuindo a Array depois de processado uma linha.
A medida que ia pegando os campos, ia cortando ela com adel...
Saudações,
Itamar M. Lins Jr.
Para busca em Array grandona (uma de 10000 rows), que é muito lento pq não é indexado, uso a função abaixo, que começa a busca do meio para as pontas e fica muito mais rápido que o velho Ascan(). Não me lembro que me passou ela.
Código: Selecionar todos
//mlinha=buscabin(aArray, srchstr, col_pesq)
Function aScan2( a, mBuscab)
Local low, high, mid, mtam_plv, x
low := 1
high:= Len(a)
If high = 0
Return(0)
EndIf
mid := Int((low + high) / 2)
mtam_plv:= Len(mbuscab)
Do While SubStr(a[mid], 1, mtam_plv) # mBuscab .and. low <= high
If mbuscab > SubStr(a[mid], 1, mtam_plv)
low := mid + 1
Else
high := mid -1
if high < 1
return(0)
EndIf
EndIf
mid := Int((low + high) / 2)
EndDo
If SubStr(a[mid], 1, mtam_plv) # mbuscab
mid := 0
Else
For x := mid -1 to 1 Step -1
if SubStr(a[x], 1, mtam_plv) = mbuscab
mid := x
Else
exit
EndIf
Next
EndIf
Return(mid)
Abraços.
ReGex para buscar TAG de XML
Enviado: 22 Nov 2024 11:49
por clodoaldomonteiro
Aqui a função ajustada para fazer busca numa array bidimensional.
Código: Selecionar todos
Function aScanB( aArray, mBuscab, nColumn )
Local low, high, mid, mtam_plv, x
Local cValue // Valor atual da coluna especificada para busca
low := 1
high := Len( aArray )
If high = 0
Return( 0 )
EndIf
mid := Int(( low + high ) / 2 )
mtam_plv := Len( mBuscab )
// Verificação adicional para garantir índice válido na coluna
If ValType( aArray[1] ) != "A" .or. nColumn > Len( aArray[1] )
Return( 0 )
EndIf
// Busca binária com base na coluna especificada
Do While .T.
cValue := SubStr( aArray[mid][nColumn], 1, mtam_plv )
If cValue # mBuscab .and. low <= high
If mBuscab > cValue
low := mid + 1
Else
high := mid - 1
If high < 1
Return( 0 )
EndIf
EndIf
mid := Int(( low + high ) / 2 )
Else
Exit
EndIf
EndDo
// Verifica se o valor encontrado é o desejado
If cValue # mBuscab
mid := 0
Else
// Busca por ocorrências anteriores na mesma coluna
For x := mid - 1 To 1 Step -1
If SubStr( aArray[x][nColumn], 1, mtam_plv ) = mBuscab
mid := x
Else
Exit
EndIf
Next
EndIf
Return( mid )
Processando um DBF de 48500 registros, com 3 relacionamentos, gerou uma matriz de 29700 registros, mais um tempo para gerar um XML, ficou em 52 segundos, e com Ascan() original fica em 14:20 minutos.
Abraços.
ReGex para buscar TAG de XML
Enviado: 22 Nov 2024 14:50
por JoséQuintas
O detalhe é que é o mesmo que comparar LOCATE com SEEK.
hb_AScan() é pra pesquisa direta, esse algorítimo só funciona se estiver tudo ordenado pelo campo chave.
Com o arquivo em ordem, 10.000 registros, ao olhar o do meio, já podemos descartar 5.000
Se não estiver ordenado não serve.