Backup: JSON optimization attempt with direct string merge

This version attempted to optimize JSON processing by:
- Direct string concatenation without deserialization
- MergeProductsJsonDirect and MergeOrdersJsonDirect functions
- Direct STRTOFILE instead of nfJsonCreate

Issues found:
- Only 100 products saved instead of 812 (merge failed)
- Invalid JSON syntax in orders: 'discounts':[,
- Pattern matching errors for GoMag API structure

Reverting to stable version 8324a26 next.

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
2025-08-27 17:22:05 +03:00
parent fd48ca480b
commit 6f362f41e7
2 changed files with 458 additions and 107 deletions

View File

@@ -28,9 +28,10 @@ SET DEFAULT TO (m.gcAppPath)
lcPath = gcAppPath + 'nfjson;' lcPath = gcAppPath + 'nfjson;'
SET PATH TO (m.lcPath) ADDITIVE SET PATH TO (m.lcPath) ADDITIVE
SET PROCEDURE TO nfjsonread.prg ADDITIVE
SET PROCEDURE TO utils.prg ADDITIVE SET PROCEDURE TO utils.prg ADDITIVE
SET PROCEDURE TO nfjsonread.prg ADDITIVE
SET PROCEDURE TO nfjsoncreate.prg ADDITIVE SET PROCEDURE TO nfjsoncreate.prg ADDITIVE
SET PROCEDURE TO regex.prg ADDITIVE
*-- Initializare logging si statistici *-- Initializare logging si statistici
gnStartTime = SECONDS() gnStartTime = SECONDS()
@@ -108,11 +109,9 @@ ENDTRY
IF llGetProducts IF llGetProducts
LogMessage("[PRODUCTS] Starting product retrieval", "INFO", gcLogFile) LogMessage("[PRODUCTS] Starting product retrieval", "INFO", gcLogFile)
*-- Bucla pentru preluarea tuturor produselor (paginare) *-- Bucla pentru preluarea tuturor produselor (paginare) - optimizare JSON direct
loAllJsonData = CREATEOBJECT("Empty") LOCAL lcAllProductsJson
ADDPROPERTY(loAllJsonData, "products", CREATEOBJECT("Empty")) lcAllProductsJson = ""
ADDPROPERTY(loAllJsonData, "total", 0)
ADDPROPERTY(loAllJsonData, "pages", 0)
lnTotalProducts = 0 lnTotalProducts = 0
DO WHILE llHasMorePages DO WHILE llHasMorePages
@@ -147,28 +146,31 @@ IF llGetProducts
*-- Success - preluare raspuns *-- Success - preluare raspuns
lcResponse = loHttp.ResponseText lcResponse = loHttp.ResponseText
*-- Parsare JSON cu nfjson *-- Optimizare: folosim JSON direct fara parsare completa
SET PATH TO nfjson ADDITIVE IF lnCurrentPage = 1
loJsonData = nfJsonRead(lcResponse) *-- Prima pagina - parsam doar pentru metadata
SET PATH TO nfjson ADDITIVE
IF !ISNULL(loJsonData) loJsonData = nfJsonRead(lcResponse)
*-- Prima pagina - setam informatiile generale
IF lnCurrentPage = 1 IF !ISNULL(loJsonData)
IF TYPE('loJsonData.total') = 'C' OR TYPE('loJsonData.total') = 'N' IF TYPE('loJsonData.total') = 'C' OR TYPE('loJsonData.total') = 'N'
loAllJsonData.total = VAL(TRANSFORM(loJsonData.total)) lnTotalProducts = VAL(TRANSFORM(loJsonData.total))
ENDIF ENDIF
IF TYPE('loJsonData.pages') = 'C' OR TYPE('loJsonData.pages') = 'N' IF TYPE('loJsonData.pages') = 'C' OR TYPE('loJsonData.pages') = 'N'
loAllJsonData.pages = VAL(TRANSFORM(loJsonData.pages)) lnTotalPages = VAL(TRANSFORM(loJsonData.pages))
ENDIF ENDIF
LogMessage("[PRODUCTS] Total items: " + TRANSFORM(loAllJsonData.total) + " | Pages: " + TRANSFORM(loAllJsonData.pages), "INFO", gcLogFile) LogMessage("[PRODUCTS] Total items: " + TRANSFORM(lnTotalProducts) + " | Pages: " + TRANSFORM(lnTotalPages), "INFO", gcLogFile)
ENDIF ENDIF
*-- Adaugare produse din pagina curenta *-- Salvam JSON-ul complet pentru prima pagina
IF TYPE('loJsonData.products') = 'O' lcAllProductsJson = lcResponse
DO MergeProducts WITH loAllJsonData, loJsonData ELSE
ENDIF *-- Paginile urmatoare - merge direct JSON
lcAllProductsJson = MergeProductsJsonDirect(lcAllProductsJson, lcResponse)
ENDIF
*-- Verificare daca mai sunt pagini *-- Verificare daca mai sunt pagini
IF lnCurrentPage = 1 AND !ISNULL(loJsonData)
IF TYPE('loJsonData.pages') = 'C' OR TYPE('loJsonData.pages') = 'N' IF TYPE('loJsonData.pages') = 'C' OR TYPE('loJsonData.pages') = 'N'
lnTotalPages = VAL(TRANSFORM(loJsonData.pages)) lnTotalPages = VAL(TRANSFORM(loJsonData.pages))
IF lnCurrentPage >= lnTotalPages IF lnCurrentPage >= lnTotalPages
@@ -180,14 +182,14 @@ IF llGetProducts
llHasMorePages = .F. llHasMorePages = .F.
ENDIF ENDIF
ENDIF ENDIF
lnCurrentPage = lnCurrentPage + 1
ELSE ELSE
*-- Eroare parsare JSON *-- Pentru paginile urmatoare, verificam daca am ajuns la limita
LogMessage("[PRODUCTS] ERROR: JSON parsing failed for page " + TRANSFORM(lnCurrentPage), "ERROR", gcLogFile) IF lnTotalPages > 0 AND lnCurrentPage >= lnTotalPages
llHasMorePages = .F. llHasMorePages = .F.
ENDIF
ENDIF ENDIF
lnCurrentPage = lnCurrentPage + 1
ELSE ELSE
*-- Eroare HTTP *-- Eroare HTTP
@@ -219,16 +221,12 @@ IF llGetProducts
ENDDO ENDDO
*-- Salvare array JSON cu toate produsele *-- Salvare JSON direct (optimizat)
IF !ISNULL(loAllJsonData) AND TYPE('loAllJsonData.products') = 'O' IF !EMPTY(lcAllProductsJson)
lcJsonFileName = lcOutputDir + "\gomag_all_products_" + DTOS(DATE()) + "_" + STRTRAN(TIME(), ":", "") + ".json" lcJsonFileName = lcOutputDir + "\gomag_all_products_" + DTOS(DATE()) + "_" + STRTRAN(TIME(), ":", "") + ".json"
DO SaveProductsArray WITH loAllJsonData, lcJsonFileName STRTOFILE(lcAllProductsJson, lcJsonFileName)
LogMessage("[PRODUCTS] JSON saved: " + lcJsonFileName, "INFO", gcLogFile) LogMessage("[PRODUCTS] JSON saved: " + lcJsonFileName, "INFO", gcLogFile)
*-- Calculam numarul de produse procesate gnProductsProcessed = lnTotalProducts
IF TYPE('loAllJsonData.products') = 'O'
lnPropCount = AMEMBERS(laProducts, loAllJsonData.products, 0)
gnProductsProcessed = lnPropCount
ENDIF
ENDIF ENDIF
ELSE ELSE
@@ -239,13 +237,11 @@ ENDIF
IF llGetOrders IF llGetOrders
LogMessage("[ORDERS] Starting orders retrieval (last " + TRANSFORM(loSettings.OrderDaysBack) + " days from " + lcStartDateStr + ")", "INFO", gcLogFile) LogMessage("[ORDERS] Starting orders retrieval (last " + TRANSFORM(loSettings.OrderDaysBack) + " days from " + lcStartDateStr + ")", "INFO", gcLogFile)
*-- Reinitializare pentru comenzi *-- Reinitializare pentru comenzi - optimizare JSON direct
lnCurrentPage = 1 lnCurrentPage = 1
llHasMorePages = .T. llHasMorePages = .T.
loAllOrderData = CREATEOBJECT("Empty") LOCAL lcAllOrdersJson
ADDPROPERTY(loAllOrderData, "orders", CREATEOBJECT("Empty")) lcAllOrdersJson = ""
ADDPROPERTY(loAllOrderData, "total", 0)
ADDPROPERTY(loAllOrderData, "pages", 0)
*-- Bucla pentru preluarea comenzilor *-- Bucla pentru preluarea comenzilor
DO WHILE llHasMorePages DO WHILE llHasMorePages
@@ -280,93 +276,63 @@ DO WHILE llHasMorePages
*-- Success - preluare raspuns *-- Success - preluare raspuns
lcResponse = loHttp.ResponseText lcResponse = loHttp.ResponseText
*-- Parsare JSON cu nfjson *-- Optimizare: folosim JSON direct pentru comenzi
SET PATH TO nfjson ADDITIVE IF lnCurrentPage = 1
loJsonData = nfJsonRead(lcResponse) *-- Prima pagina - parsam doar pentru metadata si salvam JSON-ul complet
loJsonData = nfJsonRead(lcResponse)
IF !ISNULL(loJsonData)
*-- Debug: Afisam structura JSON pentru prima pagina IF !ISNULL(loJsonData)
IF lnCurrentPage = 1
LogMessage("[ORDERS] DEBUG: Analyzing JSON structure...", "DEBUG", gcLogFile) LogMessage("[ORDERS] DEBUG: Analyzing JSON structure...", "DEBUG", gcLogFile)
lnPropCount = AMEMBERS(laJsonProps, loJsonData, 0) lnPropCount = AMEMBERS(laJsonProps, loJsonData, 0)
FOR lnDebugIndex = 1 TO MIN(lnPropCount, 10) && Primele 10 proprietati FOR lnDebugIndex = 1 TO MIN(lnPropCount, 10)
lcPropName = laJsonProps(lnDebugIndex) lcPropName = laJsonProps(lnDebugIndex)
lcPropType = TYPE('loJsonData.' + lcPropName) lcPropType = TYPE('loJsonData.' + lcPropName)
LogMessage("[ORDERS] Property: " + lcPropName + " (Type: " + lcPropType + ")", "DEBUG", gcLogFile) LogMessage("[ORDERS] Property: " + lcPropName + " (Type: " + lcPropType + ")", "DEBUG", gcLogFile)
ENDFOR ENDFOR
ENDIF
LOCAL lnTotalOrders
*-- Prima pagina - setam informatiile generale
IF lnCurrentPage = 1
IF TYPE('loJsonData.total') = 'C' OR TYPE('loJsonData.total') = 'N' IF TYPE('loJsonData.total') = 'C' OR TYPE('loJsonData.total') = 'N'
loAllOrderData.total = VAL(TRANSFORM(loJsonData.total)) lnTotalOrders = VAL(TRANSFORM(loJsonData.total))
ENDIF ENDIF
IF TYPE('loJsonData.pages') = 'C' OR TYPE('loJsonData.pages') = 'N' IF TYPE('loJsonData.pages') = 'C' OR TYPE('loJsonData.pages') = 'N'
loAllOrderData.pages = VAL(TRANSFORM(loJsonData.pages)) lnTotalPages = VAL(TRANSFORM(loJsonData.pages))
ENDIF ENDIF
LogMessage("[ORDERS] Total items: " + TRANSFORM(loAllOrderData.total) + " | Pages: " + TRANSFORM(loAllOrderData.pages), "INFO", gcLogFile) LogMessage("[ORDERS] Total items: " + TRANSFORM(lnTotalOrders) + " | Pages: " + TRANSFORM(lnTotalPages), "INFO", gcLogFile)
ENDIF
*-- Adaugare comenzi din pagina curenta
*-- API-ul GoMag returneaza un array direct de comenzi
LOCAL llHasOrders, lnOrdersFound
llHasOrders = .F.
lnOrdersFound = 0
*-- Verificam daca JSON-ul contine proprietatea orders
IF TYPE('loJsonData.orders') = 'O'
*-- Numaram comenzile din obiectul orders
LOCAL ARRAY laOrdersProps[1]
LOCAL lnOrdersCount
lnOrdersCount = AMEMBERS(laOrdersProps, loJsonData.orders, 0)
IF lnOrdersCount > 0 *-- Calculam comenzile din prima pagina
DO MergeOrdersArray WITH loAllOrderData, loJsonData LOCAL lnFirstPageOrders
llHasOrders = .T. lnFirstPageOrders = 0
lnOrdersFound = lnOrdersCount IF TYPE('loJsonData.orders') = 'O'
LogMessage("[ORDERS] Found " + TRANSFORM(lnOrdersCount) + " orders in page " + TRANSFORM(lnCurrentPage), "INFO", gcLogFile) lnFirstPageOrders = AMEMBERS(laTemp, loJsonData.orders, 0)
gnOrdersProcessed = gnOrdersProcessed + lnOrdersCount
ENDIF
ELSE
*-- JSON-ul este direct un array de comenzi (backup logic)
lnDirectProps = AMEMBERS(laDirectProps, loJsonData, 0)
IF lnDirectProps > 0
*-- Cream un obiect temporar cu structura asteptata
LOCAL loTempData
loTempData = CREATEOBJECT("Empty")
ADDPROPERTY(loTempData, "orders", loJsonData)
DO MergeOrdersArray WITH loAllOrderData, loTempData
llHasOrders = .T.
lnOrdersFound = lnDirectProps
LogMessage("[ORDERS] Found " + TRANSFORM(lnDirectProps) + " orders in page " + TRANSFORM(lnCurrentPage), "INFO", gcLogFile)
gnOrdersProcessed = gnOrdersProcessed + lnDirectProps
ENDIF ENDIF
gnOrdersProcessed = gnOrdersProcessed + lnFirstPageOrders
ENDIF ENDIF
IF !llHasOrders *-- Salvam JSON-ul complet pentru prima pagina
LogMessage("[ORDERS] WARNING: No orders found in JSON response for page " + TRANSFORM(lnCurrentPage), "WARN", gcLogFile) lcAllOrdersJson = lcResponse
ENDIF ELSE
*-- Paginile urmatoare - merge direct JSON
lcAllOrdersJson = MergeOrdersJsonDirect(lcAllOrdersJson, lcResponse)
*-- Estimare comenzi procesate din dimensiunea JSON-ului
gnOrdersProcessed = gnOrdersProcessed + INT(LEN(lcResponse) / 500)
ENDIF
*-- Verificare daca mai sunt pagini *-- Verificare daca mai sunt pagini
IF lnCurrentPage = 1 AND !ISNULL(loJsonData)
IF TYPE('loJsonData.pages') = 'C' OR TYPE('loJsonData.pages') = 'N' IF TYPE('loJsonData.pages') = 'C' OR TYPE('loJsonData.pages') = 'N'
lnTotalPages = VAL(TRANSFORM(loJsonData.pages)) lnTotalPages = VAL(TRANSFORM(loJsonData.pages))
IF lnCurrentPage >= lnTotalPages IF lnCurrentPage >= lnTotalPages
llHasMorePages = .F. llHasMorePages = .F.
ENDIF ENDIF
ELSE
*-- Daca nu avem info despre pagini, verificam daca sunt comenzi
IF !llHasOrders
llHasMorePages = .F.
ENDIF
ENDIF ENDIF
lnCurrentPage = lnCurrentPage + 1
ELSE ELSE
*-- Eroare parsare JSON pentru comenzi *-- Pentru paginile urmatoare, verificam daca am ajuns la limita
LogMessage("[ORDERS] ERROR: JSON parsing failed for page " + TRANSFORM(lnCurrentPage), "ERROR", gcLogFile) IF lnTotalPages > 0 AND lnCurrentPage >= lnTotalPages
llHasMorePages = .F. llHasMorePages = .F.
ENDIF
ENDIF ENDIF
lnCurrentPage = lnCurrentPage + 1
ELSE ELSE
*-- Eroare HTTP pentru comenzi *-- Eroare HTTP pentru comenzi
@@ -398,10 +364,10 @@ DO WHILE llHasMorePages
ENDDO ENDDO
*-- Salvare array JSON cu toate comenzile *-- Salvare JSON direct (optimizat)
IF !ISNULL(loAllOrderData) AND TYPE('loAllOrderData.orders') = 'O' IF !EMPTY(lcAllOrdersJson)
lcOrderJsonFileName = lcOutputDir + "\gomag_orders_last7days_" + DTOS(DATE()) + "_" + STRTRAN(TIME(), ":", "") + ".json" lcOrderJsonFileName = lcOutputDir + "\gomag_orders_last7days_" + DTOS(DATE()) + "_" + STRTRAN(TIME(), ":", "") + ".json"
DO SaveOrdersArray WITH loAllOrderData, lcOrderJsonFileName STRTOFILE(lcAllOrdersJson, lcOrderJsonFileName)
LogMessage("[ORDERS] JSON saved: " + lcOrderJsonFileName, "INFO", gcLogFile) LogMessage("[ORDERS] JSON saved: " + lcOrderJsonFileName, "INFO", gcLogFile)
ENDIF ENDIF
@@ -549,6 +515,123 @@ ENDIF
ENDPROC ENDPROC
*-- Functie optimizata pentru merge direct JSON produse (fara deserializare)
PROCEDURE MergeProductsJsonDirect
PARAMETERS tcFirstPageJson, tcNextPageJson
LOCAL lcEndPos, lcStart, lcEnd, lcNewProperties, lcResult
*-- Gaseste sfârșitul secțiunii products din prima pagina - cautam pozitia } inainte de "total"
lcEndPos = AT('},\"total\"', tcFirstPageJson)
IF lcEndPos = 0
*-- Fallback: gaseste ultimul } inainte de total
lcEndPos = AT('\"total\"', tcFirstPageJson)
IF lcEndPos > 0
lcEndPos = RAT('}', LEFT(tcFirstPageJson, lcEndPos - 1))
ENDIF
ENDIF
IF lcEndPos = 0
*-- Nu putem face merge, returnam prima pagina
RETURN tcFirstPageJson
ENDIF
*-- Debug logging pentru debugging
LogMessage("[PRODUCTS] DEBUG: Starting merge - Page 1 size: " + TRANSFORM(LEN(tcFirstPageJson)) + " | Next page size: " + TRANSFORM(LEN(tcNextPageJson)), "DEBUG", gcLogFile)
LogMessage("[PRODUCTS] DEBUG: Products end position found at: " + TRANSFORM(lcEndPos), "DEBUG", gcLogFile)
*-- Daca nu putem extrage cu regex, incercam metoda manuala
lcStart = AT('\"products\":{', tcNextPageJson)
IF lcStart > 0
lcStart = lcStart + 11 && dupa {"products":{
lcEnd = AT('},\"total\"', tcNextPageJson)
IF lcEnd = 0
lcEnd = AT('\"total\"', tcNextPageJson)
IF lcEnd > 0
lcEnd = RAT('}', LEFT(tcNextPageJson, lcEnd - 1))
ENDIF
ENDIF
IF lcEnd > lcStart
lcNewProperties = SUBSTR(tcNextPageJson, lcStart, lcEnd - lcStart)
LogMessage("[PRODUCTS] DEBUG: Extracted " + TRANSFORM(LEN(lcNewProperties)) + " chars from next page", "DEBUG", gcLogFile)
*-- Insereaza proprietatile cu virgula separator
lcResult = STUFF(tcFirstPageJson, lcEndPos, 0, ',' + lcNewProperties)
LogMessage("[PRODUCTS] DEBUG: Merge successful, result size: " + TRANSFORM(LEN(lcResult)), "DEBUG", gcLogFile)
RETURN lcResult
ELSE
LogMessage("[PRODUCTS] ERROR: Invalid extraction positions - Start: " + TRANSFORM(lcStart) + ", End: " + TRANSFORM(lcEnd), "ERROR", gcLogFile)
ENDIF
ENDIF
*-- Daca nu putem extrage, returnam prima pagina
RETURN tcFirstPageJson
ENDPROC
*-- Functie optimizata pentru merge direct JSON comenzi (fara deserializare)
PROCEDURE MergeOrdersJsonDirect
PARAMETERS tcFirstPageJson, tcNextPageJson
LOCAL lcEndPos, lcStart, lcEnd, lcNewOrders, lcResult
LOCAL lcTotalPos
*-- Debug logging pentru debugging
LogMessage("[ORDERS] DEBUG: Starting merge - Page 1 size: " + TRANSFORM(LEN(tcFirstPageJson)) + " | Next page size: " + TRANSFORM(LEN(tcNextPageJson)), "DEBUG", gcLogFile)
*-- Cautam pozitia pentru inserare inainte de "total", "page" sau "pages"
lcEndPos = AT('},\"total\"', tcFirstPageJson)
IF lcEndPos = 0
lcEndPos = AT('},\"page\"', tcFirstPageJson)
ENDIF
IF lcEndPos = 0
lcEndPos = AT('},\"pages\"', tcFirstPageJson)
ENDIF
IF lcEndPos = 0
LogMessage("[ORDERS] ERROR: Cannot find orders end position in first page", "ERROR", gcLogFile)
RETURN tcFirstPageJson
ENDIF
LogMessage("[ORDERS] DEBUG: Orders end position found at: " + TRANSFORM(lcEndPos), "DEBUG", gcLogFile)
*-- Extrage doar continutul din "orders":{...} din pagina urmatoare
lcStart = AT('\"orders\":{', tcNextPageJson)
IF lcStart > 0
lcStart = lcStart + 10 && dupa {"orders":{
*-- Gaseste sfarsitul obiectului orders din pagina urmatoare
lcEnd = AT('},\"total\"', tcNextPageJson)
IF lcEnd = 0
lcEnd = AT('},\"page\"', tcNextPageJson)
ENDIF
IF lcEnd = 0
lcEnd = AT('},\"pages\"', tcNextPageJson)
ENDIF
IF lcEnd > lcStart
lcNewOrders = SUBSTR(tcNextPageJson, lcStart, lcEnd - lcStart)
LogMessage("[ORDERS] DEBUG: Extracted " + TRANSFORM(LEN(lcNewOrders)) + " chars from next page", "DEBUG", gcLogFile)
*-- Curatam orice } de la final daca exista
lcNewOrders = RTRIM(lcNewOrders, ' }')
*-- Insereaza noile comenzi cu virgula
lcResult = STUFF(tcFirstPageJson, lcEndPos, 0, ',' + lcNewOrders)
LogMessage("[ORDERS] DEBUG: Merge successful, result size: " + TRANSFORM(LEN(lcResult)), "DEBUG", gcLogFile)
RETURN lcResult
ELSE
LogMessage("[ORDERS] ERROR: Invalid extraction positions - Start: " + TRANSFORM(lcStart) + ", End: " + TRANSFORM(lcEnd), "ERROR", gcLogFile)
ENDIF
ENDIF
*-- Daca nu putem extrage, returnam prima pagina
RETURN tcFirstPageJson
ENDPROC
*-- Functiile utilitare au fost mutate in utils.prg *-- Functiile utilitare au fost mutate in utils.prg
*-- Scriptul cu paginare completa pentru preluarea tuturor produselor si comenzilor *-- Scriptul cu paginare completa pentru preluarea tuturor produselor si comenzilor

268
regex.prg Normal file
View File

@@ -0,0 +1,268 @@
*!* CLEAR
*!* ?strtranx([ana are 1234567890.1234 lei], [\s\d+\.\d\s], [=TRANSFORM($1, "999 999 999 999.99")])
*?strtranx([ana are <<1234567890.1234>> lei], [<<], [=TRANSFORM($1, "AA")])
*!* RETURN
CLEAR
*-- http://www.cornerstonenw.com/article_id_parsing3.htm
SET STEP ON
lcSourceString = [ana are mere 123,345 678 ad]
LOCAL laItems[10]
lnResults = GetRegExpAll(lcSourceString, '\d+', @laItems)
SET STEP ON
RETURN
strTest = [ab cd2""$$<24>]
?strTest
?StripNonAscii(strTest)
*-- replace non a-z09 with "" case-insensitive
? strtranx([Ab ra /ca\d&abr'a],"[^a-z0-9]",[],1,,1)
RETURN
*-- count words
? OccursRegExp("\b(\w+)\b", [the then quick quick brown fox fox])
&& prints 7
*-- count repeatedwords
? OccursRegExp("\b(\w+)\s\1\b", [the then quick quick brown fox fox])
&& prints 2
*-- replace first and second lower-case "a"
? strtranx([Abracadabra],[a],[*],1,2)
&& prints Abr*c*dabra
*-- replace first and second "a" case-insensitive
? strtranx([Abracadabra],[a],[*],1,2,1)
&& prints *br*cadabra
*-- locate the replacement targets
? strtranx([Abracadabra],[^a|a$],[*],1,2,0)
&& Abracadabr*
? strtranx([Abracadabra],[^a|a$],[*],1,2,1)
&& *bracadabr*
lcText = "The cost, is $123,345.75. "
*-- convert the commas
lcText = strtranx( m.lcText, "(\d{1,3})\,(\d{1,}) ","$1 $2" )
*-- convert the decimals
? strtranx( m.lcText, "(\d{1,3})\.(\d{1,})", "$1,$2" )
** prints "The cost, is $123 345,75."
*-- add 1 to all digits
? strtranx( [ABC123], "(\d)", [=TRANSFORM(VAL($1)+1)] )
** prints "ABC234"
*-- convert all dates to long format
? strtranx( [the date is: 7/18/2004 ] , [(\d{1,2}/\d{1,2}/\d{4})], [=TRANSFORM(CTOD($1),"@YL")])
** prints "the date is: Sunday, July 18, 2004"
*----------------------------------------------------------
FUNCTION StrtranRegExp( tcSourceString, tcPattern, tcReplace )
LOCAL loRE
loRE = CREATEOBJECT("vbscript.regexp")
WITH loRE
.PATTERN = tcPattern
.GLOBAL = .T.
.multiline = .T.
RETURN .REPLACE( tcSourceString , tcReplace )
ENDWITH
ENDFUNC
*----------------------------------------------------------
FUNCTION OccursRegExp(tcPattern, tcText)
LOCAL loRE, loMatches, lnResult
loRE = CREATEOBJECT("vbscript.regexp")
WITH loRE
.PATTERN = m.tcPattern
.GLOBAL = .T.
.multiline = .T.
loMatches = loRE.Execute( m.tcText )
lnResult = loMatches.COUNT
loMatches = NULL
ENDWITH
RETURN m.lnResult
ENDFUNC
*----------------------------------------------------------
FUNCTION strtranx(tcSearched, ;
tcSearchFor, ;
tcReplacement, ;
tnStart, tnNumber, ;
tnFlag )
*-- the final version of the UDF
LOCAL loRE, lcText, lnShift, lcCommand,;
loMatch, loMatches, lnI, lnK, lcSubMatch,;
llevaluate, lcMatchDelim, lcReplaceText, lcReplacement,;
lnStart, lnNumber, loCol, lcKey
IF EMPTY(NVL(tcSearched, ''))
RETURN NVL(tcSearched, '')
ENDIF
loRE = CREATEOBJECT("vbscript.regexp")
WITH loRE
.PATTERN = m.tcSearchFor
.GLOBAL = .T.
.multiline = .T.
.ignorecase = IIF(VARTYPE(m.tnFlag)=[N],m.tnFlag = 1,.F.)
ENDWITH
lcReplacement = m.tcReplacement
*--- are we evaluating?
IF m.lcReplacement = [=]
llevaluate = .T.
lcReplacement = SUBSTR( m.lcReplacement, 2 )
ENDIF
IF VARTYPE( m.tnStart )=[N]
lnStart = m.tnStart
ELSE
lnStart = 1
ENDIF
IF VARTYPE( m.tnNumber) =[N]
lnNumber = m.tnNumber
ELSE
lnNumber = -1
ENDIF
IF m.lnStart>1 OR m.lnNumber#-1 OR m.llevaluate
lcText = m.tcSearched
lnShift = 1
loMatches = loRE.execute( m.lcText )
loCol = CREATEOBJECT([collection])
lnNumber = IIF( lnNumber=-1,loMatches.COUNT,MIN(lnNumber,loMatches.COUNT))
FOR lnK = m.lnStart TO m.lnNumber
loMatch = loMatches.ITEM(m.lnK-1) && zero based
lcCommand = m.lcReplacement
FOR lnI= 1 TO loMatch.submatches.COUNT
lcSubMatch = loMatch.submatches(m.lnI-1) && zero based
IF m.llevaluate
* "escape" the string we are about to use in an evaluation.
* it is important to escape due to possible delim chars (like ", ' etc)
* malicious content, or VFP line-length violations.
lcKey = ALLTRIM(TRANSFORM(m.lnK)+[_]+TRANSFORM(m.lnI))
loCol.ADD( m.lcSubMatch, m.lcKey )
lcSubMatch = [loCol.item(']+m.lcKey+[')]
ENDIF
lcCommand = STRTRAN( m.lcCommand, "$" + ALLTRIM( STR( m.lnI ) ) , m.lcSubMatch)
ENDFOR
IF m.llevaluate
TRY
lcReplaceText = EVALUATE( m.lcCommand )
CATCH TO loErr
lcReplaceText="[[ERROR #"+TRANSFORM(loErr.ERRORNO)+[ ]+loErr.MESSAGE+"]]"
ENDTRY
ELSE
lcReplaceText = m.lcCommand
ENDIF
lcText = STUFF( m.lcText, loMatch.FirstIndex + m.lnShift, m.loMatch.LENGTH, m.lcReplaceText )
lnShift = m.lnShift + LEN( m.lcReplaceText ) - m.loMatch.LENGTH
ENDFOR
ELSE
lcText = loRE.REPLACE( m.tcSearched, m.tcReplacement )
ENDIF
RETURN m.lcText
ENDFUNC
*=====================
FUNCTION StripNonAscii
LPARAMETERS tcSourceString, tcReplaceString
TEXT TO lcPattern NOSHOW
[^A-Za-z 0-9 \.,\?'""!@#\$%\^&\*\(\)-_=\+;:<>\/\\\|\}\{\[\]`~]
ENDTEXT
lcReplace = IIF(TYPE('tcReplaceString') <> 'C', "", tcReplaceString)
lcReturn = strtranx( m.tcSourceString, m.lcPattern, m.lcReplace,1,,1)
RETURN m.lcReturn
ENDFUNC && StripNonAscii
*=====================
* Intoarce un text care se potriveste cu pattern-ul
* Ex. Localitatea din textul: STRADA NR LOCALITATE
*=====================
FUNCTION GetRegExp
LPARAMETERS tcSourceString, tcPattern, tnOccurence
* tcSourceString: Bld. Stefan cel Mare 14 Tirgu Neamt
* tcPattern: [A-Za-z\s]+$ = (caracter sau spatiu) de cel putin o data la sfarsitul liniei = Tirgu Neamt
* tcPattern: \d+[A-Za-z\s]+$ = oricate cifre (caracter sau spatiu) de cel putin o data la sfarsitul liniei = 14 Tirgu Neamt
LOCAL loRE, loMatches, lcResult, lnOccurence
lcResult = ''
lnOccurence = IIF(!EMPTY(m.tnOccurence) and TYPE('tnOccurence') = 'N', m.tnOccurence, 1)
loRE = CREATEOBJECT("vbscript.regexp")
WITH loRE
.PATTERN = m.tcPattern
.GLOBAL = .T.
.multiline = .T.
loMatches = loRE.Execute( m.tcSourceString)
IF loMatches.COUNT >= m.lnOccurence
lcResult = loMatches.Item(m.lnOccurence - 1).Value
ENDIF
loMatches = NULL
ENDWITH
RETURN m.lcResult
ENDFUNC && GetRegExp
*=====================
* Intoarce numarul potrivirilor si un parametru OUT array sau lista de numere facturi separate prin ","
* Ex. Toate numerele dintr-un text lnMatches = GetRegExpAll(lcSourceString, '\d+', @loMatches)
*=====================
FUNCTION GetRegExpAll
LPARAMETERS tcSourceString, tcPattern, taItems
* tcSourceString: Bld. Stefan cel Mare 14 Tirgu Neamt
* tcPattern: [A-Za-z\s]+$ = (caracter sau spatiu) de cel putin o data la sfarsitul liniei = Tirgu Neamt
* tcPattern: \d+[A-Za-z\s]+$ = oricate cifre (caracter sau spatiu) de cel putin o data la sfarsitul liniei = 14 Tirgu Neamt
* taItems "A">taItems : array cu rezultatele (OUT) taItems[1..Result] sau taItems "C" lista facturi separate prin virgula
LOCAL loRE, loMatches, lnResults, lnItem
IF TYPE('taItems') = "A"
EXTERNAL ARRAY taItems
ELSE
taItems = ""
ENDIF
lnResult = 0
loRE = CREATEOBJECT("vbscript.regexp")
WITH loRE
.PATTERN = m.tcPattern
.GLOBAL = .T.
.multiline = .T.
loMatches = loRE.Execute( m.tcSourceString)
lnResults = loMatches.COUNT
IF TYPE('taItems') = "A"
DIMENSION taItems[m.lnResult]
FOR lnItem = 1 TO m.lnResult
taItems[m.lnItem] = loMatches.Item(m.lnItem-1).Value
ENDFOR
ELSE
FOR lnItem = 1 TO m.lnResults
taItems = taItems + IIF(m.lnItem > 1, ",", "") + loMatches.Item(m.lnItem-1).Value
ENDFOR
ENDIF
loMatches = NULL
ENDWITH
RETURN m.lnResults
ENDFUNC && GetRegExp