FiveTech Support Forums

FiveWin / Harbour / xBase community
Board index FiveWin para Harbour/xHarbour HIX 1.2
Posts: 1446
Joined: Mon Oct 10, 2005 02:38 PM
Re: HIX 1.2
Posted: Sat Jan 24, 2026 12:41 AM
Carles wrote:

It's seriously killing the system's performance.

Eso mismo he pensado yo cuando AI me ha ha creado unas funciones casi idénticas.

Pero si quiero que todo lo el 'montaje' web trabaje en UTF8 y a su vez no tocar la app de escritorio, ¿Cómo debería gestionarlo?
No es una pregunta expresamente para ti.

De la lectura a DBF no me puedo escapar, de la grabación se podría limitar a sólo los campos modificados.
(perdonad el desconocimiento pero ya llevo demasiadas horas esta semana)

Un Saludo

Carlos G.



FiveWin 25.12 + Harbour 3.2.0dev (r2502110321), BCC 7.7 Windows 11 Home

Posts: 6984
Joined: Fri Oct 07, 2005 07:07 PM
Re: HIX 1.2
Posted: Sat Jan 24, 2026 07:44 AM

Important clarification:
Both actions happen on the server side and inside the Harbour layer.
In my setup this logic runs inside a Harbour-based microservice; in your case it would run inside HIX. The principle is exactly the same.

Example: normalization on read (DBF → server logic)

Code (harbour): Select all Collapse
function dbf2hashLinewise( cFilePath, hTargetHash, cKeyField, cValueField, cDbfName )

   use ( cFilePath ) new
   do while .not. eof()
      hTargetHash[ ALLTRIM( &cKeyField ) ] := ;
         ALLTRIM( convertUmlaute( &cValueField ) )
      skip
   enddo
   select &cDbfName
   use

return

Here the normalization happens when reading from the DBF, entirely on the server and entirely in Harbour, before any JSON, web output, or further processing takes place.


---

Example: reverse normalization on write (server logic → DBF)

Code (harbour): Select all Collapse
function hash2dbfLinewise( cFilePath, hSourceHash, cKeyField, cValueField, cDbfName )

   use ( cFilePath ) new
   do while .not. eof()
      if hb_HHasKey( hSourceHash, ALLTRIM( &cKeyField ) )
         replace &cValueField with ;
            ALLTRIM( revertUmlaute( hSourceHash[ ALLTRIM( &cKeyField ) ] ) )
      endif
      skip
   enddo
   select &cDbfName
   use

return

The reverse operation also happens server-side in Harbour, and only when data is actually written back. The desktop application remains untouched, and the DBF stays in its historical format.


---

About performance (this is important)

In practice, this conversion is so fast that you cannot meaningfully measure it using the time units normally available in Harbour, such as Seconds() or even millisecond-level timers. In real-world operation it simply does not show up as a bottleneck.

If you really want to measure it, you would need to use high-resolution OS-level APIs (for example via QueryPerformanceCounter()), because the runtime is below what Harbour’s standard timing functions can reliably capture.

In other words:
this normalization step is theoretically visible, but practically irrelevant in terms of performance compared to DBF I/O, locking, indexing, rendering, or network latency.


---

The core idea

“No matter where this byte comes from —
when I see it, I know what the user meant.”

That is data hygiene, not a hack.


---

Why this is necessary

When the historical DBF data is mixed at the byte level — different codepages, different tools, different decades — codepage primitives alone cannot fully solve the problem, because they assume a clean and uniform origin.

In such cases, an explicit normalization step at the server boundary is the only way to regain determinism. Once the data is normalized, converting to UTF-8 for web/JSON is trivial and predictable.

Whether this logic lives inside a standalone Harbour microservice or inside HIX does not change the concept — only the hosting environment.

Posts: 6984
Joined: Fri Oct 07, 2005 07:07 PM
Re: HIX 1.2
Posted: Sat Jan 24, 2026 08:15 AM

I thought about your question again, and I think the core issue is actually JSON, not just UTF-8.

You have to be careful here: correct UTF-8 conversion alone is not enough to guarantee valid JSON.

JSON is stricter than plain text. Even after ANSI → UTF-8 conversion, a string may still contain characters that are not allowed in JSON, such as hidden control characters from old DBFs, embedded line breaks, quotes, or backslashes. These often survive encoding conversion but can break JSON parsing or cause inconsistent behavior in the browser.

That’s why it’s important to separate three steps clearly:

  1. Normalize historical data (clean up known byte-level artifacts)
  2. Convert encoding (ANSI → UTF-8)
  3. Serialize safely to JSON

---

Test example 1: UTF-8 is correct, JSON is broken

Code (harbour): Select all Collapse
cAnsi := "Text with quote: "" and CRLF" + Chr(13) + Chr(10)
cUtf8 := hb_StrToUTF8( cAnsi )

// ❌ Dangerous: manual JSON building
cJson := '{ "text": "' + cUtf8 + '" }'

This string may be valid UTF-8, but the unescaped quote and control characters make the JSON invalid or unreliable.


---

Test example 2: Correct and safe JSON serialization

Code (harbour): Select all Collapse
cAnsi := "Text with quote: "" and CRLF" + Chr(13) + Chr(10)
cUtf8 := hb_StrToUTF8( cAnsi )

h := { "text" => cUtf8 }
cJson := hb_jsonEncode( h )

Here:

  • quotes are escaped correctly
  • control characters are handled properly
  • the result is valid JSON

---

Test example 3: Detect hidden control characters

Code (harbour): Select all Collapse
function hasControlChars( cText )
   local i
   for i := 1 to Len( cText )
      if Asc( SubStr( cText, i, 1 ) ) < 32
         return .T.
      endif
   next
return .F.

Use this to check whether a string contains invisible bytes that may cause problems in JSON or JavaScript.


---

Recommended minimal pipeline

Code (harbour): Select all Collapse
cNormalized := convertUmlaute( cAnsi )   // data hygiene
cUtf8       := hb_StrToUTF8( cNormalized )
cJson       := hb_jsonEncode( { "value" => cUtf8 } )

And never:

Code (harbour): Select all Collapse
cJson := '{ "value": "' + cUtf8 + '" }'

---

Key takeaway

UTF-8 correctness does not guarantee JSON correctness.

With historically grown DBF data, it’s worth explicitly testing for JSON-unsafe characters. Once that boundary is clean, the rest of the web stack behaves predictably.


---
Posts: 1283
Joined: Fri Feb 10, 2006 02:34 PM
Re: HIX 1.2
Posted: Sat Jan 24, 2026 07:21 PM

Carlos,

FiveWiDi wrote:

Pero si quiero que todo lo el 'montaje' web trabaje en UTF8 y a su vez no tocar la app de escritorio, ¿Cómo debería gestionarlo?
No es una pregunta expresamente para ti.

Intento resumirlo el máximo posible, pero es bastante extenso, al final es un tema de como funciona correctamente los codepage de harbour. A mucha gente no le importa y al final pues hacen su pequeña rutina de conversión y ya lo tienen solucionado, pero no son las maneras correctas, no es una solución técnica correcta sino una solución chapuza.

Cuando creamos una aplicación en Harbour definimos un codepage , cuya función es gestionar ese mapa de caracteres, crear las reglas que nos servirá para ordenar índices, etc… Podemos usar por ejemplo HB_SetCodePage ( "ESWIN" ) en tu caso.

Nuestro objetivo es que las dbf estén limpias y saneadas. En cualquier proyecto se han de dejar siempre limpias e “higienizadas” antes poner en marcha el sistema.

Cuando abramos nuestra dbf con cualquier editor se habrían de ver absolutamente todos los códigos correctamente.

Escenario de vuestro problema -> Quiero usar una dbf que es parte de una aplicación de escritorio, pero también la quiero usar para una aplicación web, un caso típico.

Caso A – Codificar en UTF8

En el caso de que por lo que sea queráis usar una web codificada con utf-8 es necesario codificar la salida de datos hacia la web (toda la pagina web, incluido los datos de la dbf) a utf-8.

Para eso simplemente leyendo el contenido de la dbf compartida de los campos y creando la conversión con hb_StrToUtf8() es completamente suficiente, no necesitas nada mas.

Cuidado aquí, que cuando digo la salida hacia la web, porque a parte de los datos de la dbf, la codificación de la página ha de estar con utf8, esto lo debéis gestionar con el editor. Si en la página tenéis símbolos especiales, por ejemplo àèòñç… y la pagina está codificada en ansi, al viajar hacia el ordenador del cliente, intentara codificar a utf8 esa pagina y seria un desastre. Por esto es muy importante codificar con nuestro editor en utf8.

En el caso de Otto, por si lee este post, si tiene una dbf con su mapa de caracteres ‘DE’ , haciendo en su aplicación

HB_LANGSELECT('DE')        

HB_SetCodePage ( "DEWIN" )

Y al leer su dbf, en cada campo aplicar la conversión hb_strtoutf8(), ya seria suficiente

cName := Hb_StrToUtf8( FIELD->name )

Hasta aquí ya tenemos la primer parte DBF -> Convertimos a UTF8 -> HTML

Ahora viene una de las partes mas importante y atañe al conocimiento de como funcionan los codepages. Cuando usamos HB_LANGSELECT() indicamos el idioma de nuestro sistema, por defecto cojerá un codepage asociado al idioma a no ser que lo seleccionemos. Si no indicamos nada en la aplicación el codepage es ‘EN’.

Si tenemos un simple formulario en web con utf8

En este caso si nosotros convertimos la cadena que nos viene de la web de utf8 a string cojeria el mapa de caracteres ‘EN’ y en nuestra dbf nos aparecería

En este caso podríamos forzar nuestra dbf a que se salve con el codepage ‘DEWIN’ independientemente del codepage que use nuestra aplicación que ahora es ‘EN’ usando la clausula codepage

use ( hb_dirbase() + 'codepage/test4.dbf' ) EXCLUSIVE NEW CODEPAGE 'DEWIN'

En este caso la transformación quedaría perfecta

Müller Franz Pfälzerstraße

HTML -> Convertimos de Utf8 a string -> DBF

Para no tener que forzar a abrir las tablas dbf con el codepage que necesitemos usaremos:

SET( _SET_DBCODEPAGE, 'DEWIN' )

Esto fuerza a que nuestras dbf por defecto a no ser que indiquemos lo contrario usen este codepage. La configuración por defecto en nuestra aplicación al arrancar podría ser en este caso esta:

HB_LANGSELECT('DE')        

HB_SetCodePage ( "DEWIN" )	
SET( _SET_DBCODEPAGE, 'DEWIN' )

Resumen:

Configurar bien el codepage que usaremos en nuestra aplicación

Lectura para la web -> <cVar> := hb_StrToUtf8( <cField> )
Lectura web a dbf -> FIELD->name := hb_Utf8ToStr( <cParam> )

Caso B – Codificar en ISO

Si en usaras una dbf en la que tendrás tu mapa de caracteres de siempre y no piensas grabar caracteres especiales fuera de tu “lenguaje” natural… para que usar utf-8 ?.

UTF-8 es recomendado para usar hoy en día en la web, pero no significa que podemos usar otro sistema que se adapte mejor a nuestras necesidades. Imaginemos que solo queremos una web de información que se nutre de información de nuestra dbf. O un simple formulario de entrada de datos, o algún caso que se pudiera adaptar a nuestro escenario. Podríamos perfectamente codificar la web con el mismo charset que usamos y listos. NO necesitaríamos recodificar nada para publicar en la web.

En este caso, simplemente creando nuestra web (codificada con nuestro editor con ansi) con un meta ISO asi:

<meta charset='ISO-8859-1'>

Es suficiente para mostrar tal cual nuestra dbf, y si creáramos un simple formulario en esa web, el navegador enviaría los datos del formulario en ISO-8859-1

Que significa esto ? Si en este escenario nosotros iniciamos nuestra aplicación con nuestro codepage y el manejo de la dbf, NO necesitarías convertir el dato para pasarlo a la web y NO necesitarías convertir de la web los datos para salvarlo en la dbf

DBF -> HTML
HTML -> DBF

Se ha de estudiar bien cada caso y saber las opciones que tenemos y nos encontraremos, siempre que se pueda se ha de codificar en utf8, mas si usaremos técnicas como ajax, interfases con apis, … pero esto no implica que no podamos usar varias metodologías.

HIX – Configuracion

HIX ya configura todo esto. Con el comando “harbour” podemos definir nuestro lenguaje. Esto configurara el lenguaje que se usara en HIX, su codepage y configurara como tratara por defecto los dbfs

Creé hace tiempo en el manual unos capítulos sobre el charset, que aunque esta enfocado a Tweb el contenido es completamente valido para entender aun mas todo este tema. Lo podeis encontrar en https://carles9000.github.io/index_doc_en.html?search=TWEB%20manual , buscad en el índice “charset”

Final del "simposium" :D

En Harbour tenemos la capacidad de encontrar soluciones de muchas maneras, pero se trata de aprender con el tiempo como funciona internamente y evitar malas practicas que si nos pueden salvar de un apuro no son la mejor recomendación. Yo recomiendo ir aprendiendo correctamente esta manera de trabajar, entender el porque y trabajar la web con utf8. Si entendemos como funciona harbour simplemente con hb_StrToUtf8() / hb_Utf8ToScr() tenemos suficiente y sino… algo mal estamos haciendo.

Recomiendo crear pruebas con estos casos para acabar de asimilar bien los conceptos... :wink:

C.

Salutacions, saludos, regards

"...programar es fácil, hacer programas es difícil..."

UT Page -> https://carles9000.github.io/
Forum UT -> https://discord.gg/bq8a9yGMWh
HIX -> https://github.com/carles9000/hix
Posts: 6984
Joined: Fri Oct 07, 2005 07:07 PM
Re: HIX 1.2
Posted: Sat Jan 24, 2026 08:56 PM

Charly,
I want to be very clear here, because this is an important practical distinction.

Yes, if a DBF has a clean, uniform history and if all data has always been written through correctly configured applications using the same codepage, then the approach you describe with HB_LANGSELECT(), HB_SetCodePage() and hb_StrToUtf8() / hb_Utf8ToStr() is technically correct.

Unfortunately, that assumption does not hold in real-world systems.

In our case, and in many long-living DBF installations, the starting state is not clean:

  • data has been written over decades,
  • by DOS programs (OEM),
  • by Windows programs (ANSI),
  • by imports,
  • by copy & paste from browsers, emails, PDFs,
  • and today even by web clients sending UTF-8 plus CR/LF and hidden characters.

Once that happens, codepage logic alone is no longer sufficient, because codepage conversion assumes a known and consistent source encoding. When the source is mixed at the byte level, the conversion cannot be correct by definition.

That is why, in practice, we had to introduce an explicit normalization step on the server side. Not to replace Harbour’s codepage handling, but to regain determinism in historically grown data. Only after that normalization does it make sense to convert to UTF-8 for web/JSON and back when writing to DBF.

So this is not about “doing things the wrong way” or ignoring how Harbour works. It is about acknowledging that the data reality no longer matches the ideal model.

And for systems that have been alive for 20–30 years, with multiple clients and modern copy & paste workflows, that mess is the rule, not the exception.

That is the context in which our approach exists.

Best regards,
Otto

Posts: 1283
Joined: Fri Feb 10, 2006 02:34 PM
Re: HIX 1.2
Posted: Sun Jan 25, 2026 07:33 AM

Otto,

Therefore, my recommendation is to thoroughly clean the data, migrate it, and continue with best practices.

I've explained how to work correctly in both cases and avoid bad practices.

C.

Salutacions, saludos, regards

"...programar es fácil, hacer programas es difícil..."

UT Page -> https://carles9000.github.io/
Forum UT -> https://discord.gg/bq8a9yGMWh
HIX -> https://github.com/carles9000/hix
Posts: 1446
Joined: Mon Oct 10, 2005 02:38 PM
Re: HIX 1.2
Posted: Sun Jan 25, 2026 08:45 AM
Carles wrote:

Otto,
Therefore, my recommendation is to thoroughly clean the data, migrate it, and continue with best practices.
I've explained how to work correctly in both cases and avoid bad practices.
C.

Miraré el link de TWeb.
Muchísimas gracias Carles,

Un Saludo

Carlos G.



FiveWin 25.12 + Harbour 3.2.0dev (r2502110321), BCC 7.7 Windows 11 Home

Posts: 6984
Joined: Fri Oct 07, 2005 07:07 PM
Re: HIX 1.2
Posted: Sun Jan 25, 2026 10:06 AM

Charly,

I think this is also important for the Harbour ecosystem itself: we should explicitly show alternatives for developers who do not want to depend on HIX.

That does not mean HIX is wrong or useless. It means that different developers have different constraints, goals, and learning paths. Some will be comfortable building directly on HIX, others will prefer a small Harbour microservice, a standard web server, or a service-based approach around Harbour.

If Harbour is to remain attractive and future-proof, it should be possible to apply the same core ideas — data access, business logic, determinism — without being tied to a single server or framework.

Showing multiple viable paths does not weaken Harbour. It strengthens it, because it reduces lock-in, lowers entry barriers, and makes experimentation safer.

In that sense, HIX should be seen as one option, not as a prerequisite. Making that explicit helps developers choose what fits their situation, instead of feeling forced into a single model.

Best regards, Otto

Posts: 1446
Joined: Mon Oct 10, 2005 02:38 PM
Re: HIX 1.2 UWrite( &quot;Hola&quot;)
Posted: Wed Jan 28, 2026 12:12 PM

Carles,

Por que si hago esto:

UWrite( "Replacing files..." + old->( FieldGet( nContadorb ) ) +"<br>" ) UWrite( "Replacing files..." + Mi_StrToUtf8( old->( FieldGet( nContadorb ) ) ) +"<br>" ) UWrite( "Replacing files..." + Mi_Utf8ToStr( Mi_StrToUtf8( old->( FieldGet( nContadorb ) ) ) ) +"<br>" )

En ninguno de los 3 casos se ven bien en pantalla los caracteres acentuados?

Hix está con Lenguaje ES Las funciones hacen un Return HB_StrToUTF8() y Return HB_UTF8ToStr() El fichero PRG que usa UWrite() está en UTF8. Si abro el fichero DBF con codificación Windows1252/WinLatin1 se ven bien los caracteres acentuados.

Se trata de un PRG que sólo actualiza campos en un DBF, no muestra páginas ni mensajes en pantalla, a 'nivel web' la única instrucción/proceso es UWrite()

¿Es cuestión de que al navegador no se le ha indicado en que codificación recibirá lo que deberá mostrar?

¿Qué me estoy dejando?

Nota: no me preocupa, es un proceso de mantenimiento/actualización que lanzo yo a voluntad.

Gracias,

Un Saludo

Carlos G.



FiveWin 25.12 + Harbour 3.2.0dev (r2502110321), BCC 7.7 Windows 11 Home

Posts: 6984
Joined: Fri Oct 07, 2005 07:07 PM
Re: HIX 1.2
Posted: Wed Jan 28, 2026 02:52 PM

The browser does not know which encoding it should use to interpret the output. UWrite() only writes raw bytes into the HTTP response. Without an explicit HTTP header, everything the browser does is guesswork.

Posts: 1283
Joined: Fri Feb 10, 2006 02:34 PM
Re: HIX 1.2
Posted: Wed Jan 28, 2026 06:17 PM

Carlos,

FiveWiDi wrote:

¿Qué me estoy dejando?

Prueba de poner una cabecera, al menos esto

? '<meta charset="utf-8">'

C.

Salutacions, saludos, regards

"...programar es fácil, hacer programas es difícil..."

UT Page -> https://carles9000.github.io/
Forum UT -> https://discord.gg/bq8a9yGMWh
HIX -> https://github.com/carles9000/hix
Posts: 1446
Joined: Mon Oct 10, 2005 02:38 PM
Re: HIX 1.2
Posted: Wed Jan 28, 2026 06:50 PM
Carles wrote:

Carlos,

¿Qué me estoy dejando?

Prueba de poner una cabecera, al menos esto
? '<meta charset="utf-8">'
C.

Ya voy aprendiendo.
Precisamente esto mismo iba a probar, y funciona:
UWrite( '<meta charset="utf-8">' + "Replacing files..." + Mi_StrToUtf8( old->( FieldGet( nContadorb ) ) ) +"<br>" )

Moltes gràcies,

Un Saludo

Carlos G.



FiveWin 25.12 + Harbour 3.2.0dev (r2502110321), BCC 7.7 Windows 11 Home

Posts: 670
Joined: Wed Oct 19, 2005 06:41 PM
Re: HIX 1.2
Posted: Sat Feb 07, 2026 04:00 PM

buenos dias compañeros
SOLO si a alguien le sriva estoy bajando una informacion de la web mediante una url (estoy usando HB_CURL) imagino la info viene en utf8 esta info la estoy guardando en una dbf pura y dura luego de varios tropiezos la mejor solucion para pasar de utf8 a dbf ( oem ) ha sido esta funcion

function wg_utf8tooem( C )

c = hb_ansitoOem( hb_UTF8ToStr( c ) )

c = strtran( c, chr(190), chr(165) )
c = strtran( c, chr(63) , chr(144)  )

return c

OJO esto es usando el metodo prueba->error
aclaro que la info viene en mayusculas asi que la funcion solo corrige la Ñ y la É es decir solo lo que veien ene sa data habria que ampliar la funcion para mas caracteres como á, etc
si a alguien le sirve
una abrazo
WIlson

Wilson 'W' Gamboa A
Wilson.josenet@gmail.com

Continue the discussion