Files
comun/docs/conventie_encoding_cp1252.md
Marius Mutu 6de489950c sync SVN r18016: editare articole in factura de vanzare, precizie pret achizitie
ROAFACTURARE 2.11.15 (r18015): programe/ofacturare_editare.prg, programe/ofacturare_stoc.prg.
docs: conventia de encoding .sc2/.vc2 corectata la cp1250; cercetarile trans-proiect mutate
din ROAFACTURARE in docs/cercetare (r18014); actualizari reguli_lucru, oracle_export,
flux-editare-vfp-text, scripturi-migrare-db, flux-modificare-stergere-nota-jurnal, todos.
.gitignore: watchdog_out si PNG-urile din rularile headless (r18008).
Text FoxBin2Prg regenerat: clase/comun.vc2, ferestre/frm_import_note_facturi_clienti.sc2,
ferestre/frm_initializare_facturi_balanta.sc2.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01AV7dQbTLAqvrtQbH7zxXxw
2026-08-20 14:07:12 +03:00

5.2 KiB

Capcană: corupere diacritice la editarea .sc2/.vc2

Partajat între proiectele ROA* — capcana ține de formatul .sc2/.vc2 produs de FoxBin2Prg, nu de un proiect anume.

De ce se întâmplă

Fișierele .sc2/.vc2 sunt cp1250 (Windows-1250, Europa Centrală), nu UTF-8 — deși antetul FoxBin2Prg declară CPID="1252". Eticheta din antet nu corespunde conținutului: controalele care poartă text cu diacritice au FontCharSet = 238 (Europa de Est), adică VFP le randează tot ca cp1250. Diferența nu e cosmetică — un ă real scris prin cp1252 nu are echivalent în acel codepage și s-ar pierde; scris prin cp1250 (encoding-ul real al fișierului) e pur și simplu octetul 0xE3, corect și stabil.

Dacă un editor/tool citește fișierul ca UTF-8, fiecare byte >= 0x80 (diacritic) devine invalid ca secvență UTF-8 și e înlocuit cu U+FFFD. Dacă tool-ul apoi salvează tot ca UTF-8, acel U+FFFD e scris ca 3 octeți (0xEF 0xBF 0xBD) — care, citiți înapoi ca cp1250 (cum face FoxBin2Prg la write-back), apar ca �. Caracterul original e ireversibil pierdut (recuperabil DOAR din altă sursă: git history, backup).

Octeții diacriticelor din codebase-ul ROA, cu adevărul (cp1250) și aparența dacă îi decodezi greșit ca cp1252:

Literă românească Byte Aparență dacă decodezi ca cp1252
ă 0xE3 ã (a-tilde)
Ă 0xC3 Ã
î 0xEE î (identic — zonă comună cp1250/cp1252)
Î 0xCE Î (identic — zonă comună cp1250/cp1252)
â 0xE2 â (identic — zonă comună cp1250/cp1252)
ț 0xFE þ (thorn minuscul)
Ș (majusculă) 0xAA ª (ordinal masculin)
ș (minusculă) 0xBA º (ordinal feminin)

Decodate cp1250 (encoding-ul real), aceste litere sunt diacritice românești normale — exact ce apare în formular când fișierul e sănătos. Par "gunoi"/typo (ã, þ, ª, º) doar dacă le decodezi cu codepage-ul greșit — nu le "corecta" fără să verifici encoding-ul real.

Cum se verifică dacă un fișier .sc2/.vc2 e corupt

Verificarea se face pe octeți, nu pe text decodat: un codepage single-byte (1250 sau 1252) mapează fiecare octet la un caracter, deci nu produce niciodată U+FFFD — o căutare de [char]0xFFFD în textul decodat nu găsește nimic nici pe un fișier stricat. Ce se caută e secvența de trei octeți pe care a lăsat-o tool-ul care a stricat fișierul:

$b = [System.IO.File]::ReadAllBytes("<path>.vc2")
$n = 0
for ($i = 0; $i -lt $b.Length - 2; $i++) {
    if ($b[$i] -eq 0xEF -and $b[$i+1] -eq 0xBF -and $b[$i+2] -eq 0xBD) { $n++; $i += 2 }
}
"EF BF BD (U+FFFD) x $n"   # orice valoare > 0 = fisier stricat

Echivalentul din Bash, util și pentru censul complet înainte/după orice editare (vezi reguli_lucru.md, punctul 7):

od -An -tx1 <path>.vc2 | tr ' ' '\n' | grep -v '^$' | awk '$1>"7f"' | sort | uniq -c

Censul crește la stricare — un octet cp1250 devine trei octeți de U+FFFD — deci nu urmări doar scăderile. Decodat cp1250, EF BF BD apare ca ďż˝ în caption.

Cum se repară

  1. Nu edita orbește peste corupere — caracterul original e deja pierdut din fișierul curent.
  2. Dacă corupera s-a întâmplat într-o sesiune anterioară necomisă (HEAD-ul git încă e curat): recuperează originalul convertind binarul din HEAD în text:
    git show HEAD:<cale>.vcx > /tmp/head.vcx
    git show HEAD:<cale>.vct > /tmp/head.vct   # (sau .VCT, dupa caz)
    powershell -File D:\ROA\UTIL\foxbin2prg\vcx2txt.ps1 -Source /tmp/head.vcx -CacheRoot /tmp/head_out
    
    Dacă și HEAD e deja corupt (corupera a fost comisă), caută ultima versiune curată cu git cat-file blob <commit>:<cale> (din Bash, niciodată cu > din PowerShell 5.1 — adaugă BOM și re-encodează) sau svn cat <fișier> — ambele surse independente de encoding-ul curent al fișierului. Apoi decodează ambele fișiere (versiunea curată + curent corupt) ca cp1250 și compară linie cu linie (PowerShell, nu diff/Bash direct — nu decodează cp1250 corect la afișare, deși detectează totuși diferența de octeți).
  3. Reconstruiește fișierul text pornind de la versiunea curată, reaplicând DOAR modificările intenționate din sesiunea curentă (nu invers — nu cârpi fișierul corupt caracter cu caracter).
  4. Scrie rezultatul înapoi ca bytes cp1250 ([System.Text.Encoding]::GetEncoding(1250).GetBytes(...)), NU ca string UTF-8/Out-File implicit.
  5. Rulează write-back (txt2vcx.ps1 -Force) și verifică fidelity check = zero diferențe.

Cum se evită pe viitor

Când editezi .sc2/.vc2 cu tool-uri text (Read/Edit/Write), verifică întâi dacă fișierul conține diacritice (ă, ț, Ș, ș, î, â etc.) — dacă da, orice rescrie completă trebuie făcută la nivel de octeți cu encoding cp1250 explicit, nu presupunând UTF-8. Editările punctuale (Edit tool, string replace) sunt sigure DOAR dacă tool-ul păstrează restul fișierului byte-identic (nu rescrie tot fișierul reîncodat).

Vezi și

PROMPT_cautare_vfp.md din acest folder, pasul 3.c — menționează riscul pe scurt; acest fișier e detalierea completă (tabel de caractere, pași de recuperare din git).