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
IF lnCurrentPage = 1
*-- Prima pagina - parsam doar pentru metadata
SET PATH TO nfjson ADDITIVE SET PATH TO nfjson ADDITIVE
loJsonData = nfJsonRead(lcResponse) loJsonData = nfJsonRead(lcResponse)
IF !ISNULL(loJsonData) IF !ISNULL(loJsonData)
*-- 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'
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
*-- Paginile urmatoare - merge direct JSON
lcAllProductsJson = MergeProductsJsonDirect(lcAllProductsJson, lcResponse)
ENDIF 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,94 +276,64 @@ 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
*-- Prima pagina - parsam doar pentru metadata si salvam JSON-ul complet
loJsonData = nfJsonRead(lcResponse) loJsonData = nfJsonRead(lcResponse)
IF !ISNULL(loJsonData) IF !ISNULL(loJsonData)
*-- Debug: Afisam structura JSON pentru prima pagina
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
*-- Prima pagina - setam informatiile generale LOCAL lnTotalOrders
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
LogMessage("[ORDERS] Total items: " + TRANSFORM(loAllOrderData.total) + " | Pages: " + TRANSFORM(loAllOrderData.pages), "INFO", gcLogFile)
ENDIF ENDIF
LogMessage("[ORDERS] Total items: " + TRANSFORM(lnTotalOrders) + " | Pages: " + TRANSFORM(lnTotalPages), "INFO", gcLogFile)
*-- Adaugare comenzi din pagina curenta *-- Calculam comenzile din prima pagina
*-- API-ul GoMag returneaza un array direct de comenzi LOCAL lnFirstPageOrders
LOCAL llHasOrders, lnOrdersFound lnFirstPageOrders = 0
llHasOrders = .F.
lnOrdersFound = 0
*-- Verificam daca JSON-ul contine proprietatea orders
IF TYPE('loJsonData.orders') = 'O' IF TYPE('loJsonData.orders') = 'O'
*-- Numaram comenzile din obiectul orders lnFirstPageOrders = AMEMBERS(laTemp, loJsonData.orders, 0)
LOCAL ARRAY laOrdersProps[1]
LOCAL lnOrdersCount
lnOrdersCount = AMEMBERS(laOrdersProps, loJsonData.orders, 0)
IF lnOrdersCount > 0
DO MergeOrdersArray WITH loAllOrderData, loJsonData
llHasOrders = .T.
lnOrdersFound = lnOrdersCount
LogMessage("[ORDERS] Found " + TRANSFORM(lnOrdersCount) + " orders in page " + TRANSFORM(lnCurrentPage), "INFO", gcLogFile)
gnOrdersProcessed = gnOrdersProcessed + lnOrdersCount
ENDIF ENDIF
gnOrdersProcessed = gnOrdersProcessed + lnFirstPageOrders
ENDIF
*-- Salvam JSON-ul complet pentru prima pagina
lcAllOrdersJson = lcResponse
ELSE ELSE
*-- JSON-ul este direct un array de comenzi (backup logic) *-- Paginile urmatoare - merge direct JSON
lnDirectProps = AMEMBERS(laDirectProps, loJsonData, 0) lcAllOrdersJson = MergeOrdersJsonDirect(lcAllOrdersJson, lcResponse)
IF lnDirectProps > 0 *-- Estimare comenzi procesate din dimensiunea JSON-ului
*-- Cream un obiect temporar cu structura asteptata gnOrdersProcessed = gnOrdersProcessed + INT(LEN(lcResponse) / 500)
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
IF !llHasOrders
LogMessage("[ORDERS] WARNING: No orders found in JSON response for page " + TRANSFORM(lnCurrentPage), "WARN", gcLogFile)
ENDIF 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
ENDIF
ELSE ELSE
*-- Daca nu avem info despre pagini, verificam daca sunt comenzi *-- Pentru paginile urmatoare, verificam daca am ajuns la limita
IF !llHasOrders IF lnTotalPages > 0 AND lnCurrentPage >= lnTotalPages
llHasMorePages = .F. llHasMorePages = .F.
ENDIF ENDIF
ENDIF ENDIF
lnCurrentPage = lnCurrentPage + 1 lnCurrentPage = lnCurrentPage + 1
ELSE
*-- Eroare parsare JSON pentru comenzi
LogMessage("[ORDERS] ERROR: JSON parsing failed for page " + TRANSFORM(lnCurrentPage), "ERROR", gcLogFile)
llHasMorePages = .F.
ENDIF
ELSE ELSE
*-- Eroare HTTP pentru comenzi *-- Eroare HTTP pentru comenzi
LogMessage("[ORDERS] HTTP Error " + TRANSFORM(lnStatusCode) + ": " + lcStatusText + " on page " + TRANSFORM(lnCurrentPage), "ERROR", gcLogFile) LogMessage("[ORDERS] HTTP Error " + TRANSFORM(lnStatusCode) + ": " + lcStatusText + " on page " + TRANSFORM(lnCurrentPage), "ERROR", gcLogFile)
@@ -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