sync SVN r18041
This commit is contained in:
@@ -1,101 +1,57 @@
|
||||
# Capcană: corupere diacritice la editarea `.sc2`/`.vc2`
|
||||
# Capcana: corupere diacritice la editarea .sc2/.vc2
|
||||
|
||||
Partajat între proiectele ROA* — capcana ține de formatul `.sc2`/`.vc2` produs de FoxBin2Prg,
|
||||
nu de un proiect anume.
|
||||
Comun tuturor proiectelor ROA* - tine de formatul .sc2/.vc2 (FoxBin2Prg), nu de un proiect anume.
|
||||
|
||||
## De ce se întâmplă
|
||||
## Faptul
|
||||
|
||||
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.
|
||||
Fisierele .sc2/.vc2 sunt cp1250 (Europa Centrala), nu UTF-8, desi antetul FoxBin2Prg declara
|
||||
CPID="1252" - eticheta din antet nu e adevarul despre encoding. Controalele cu text diacritic
|
||||
au FontCharSet = 238.
|
||||
|
||||
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).
|
||||
## Tabelul de octeti (cp1250 real -> aparenta daca decodezi gresit ca cp1252)
|
||||
|
||||
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 |
|
||||
| Litera | Byte | Aparenta cp1252 gresita |
|
||||
|---|---|---|
|
||||
| ă | `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) |
|
||||
| a cu caciula minuscul | 0xE3 | a-tilde |
|
||||
| A cu caciula majuscul | 0xC3 | A-tilde |
|
||||
| i cu caciula minuscul | 0xEE | identic (zona comuna cp1250/cp1252) |
|
||||
| I cu caciula majuscul | 0xCE | identic (zona comuna cp1250/cp1252) |
|
||||
| a circumflex | 0xE2 | identic (zona comuna cp1250/cp1252) |
|
||||
| t cu virgula minuscul | 0xFE | thorn minuscul |
|
||||
| S cu virgula majuscul | 0xAA | ordinal masculin |
|
||||
| s cu virgula 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.
|
||||
Nu "corecta" un s-cu-virgula crezand ca e typo - e litera corecta in cp1250.
|
||||
|
||||
## Cum se verifică dacă un fișier `.sc2`/`.vc2` e corupt
|
||||
## Regula operationala
|
||||
|
||||
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:
|
||||
Orice scriere cu un tool care nu pastreaza octetii nativ (Edit/Write) corupe TOATE caracterele
|
||||
>= 0x80 din TOT fisierul (0xBA -> EF BF BD), chiar la o singura linie schimbata, si se repeta
|
||||
la FIECARE scriere. Nu re-encoda niciodata fisierul intreg: lucreaza pe octeti
|
||||
([IO.File]::ReadAllBytes/WriteAllBytes cu GetEncoding(1250)) sau scrie strict ASCII in
|
||||
continutul nou. Editeaza tot ce ai de editat, verifica, repara o singura data DUPA ultima
|
||||
scriere.
|
||||
|
||||
```powershell
|
||||
$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):
|
||||
## Verificare (cens de octeti)
|
||||
|
||||
```bash
|
||||
od -An -tx1 <path>.vc2 | tr ' ' '\n' | grep -v '^$' | awk '$1>"7f"' | sort | uniq -c
|
||||
od -An -tx1 <fisier> | 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.
|
||||
La stricare censul CRESTE (un octet devine trei) - nu urmari doar scaderile. Cauta explicit
|
||||
secventa `EF BF BD`, nu tiparul generic `C3`/`C4`/`C5`/`C8` (acela apare si in UTF-8 legitim
|
||||
din cod).
|
||||
|
||||
## Cum se repară
|
||||
## Reparare
|
||||
|
||||
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:
|
||||
```bash
|
||||
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.
|
||||
`EF BF BD` nu spune ce caracter s-a pierdut (toate diacriticele devin la fel) - inlocuirea
|
||||
oarba cu 0xBA strica alte litere din alte pozitii. Ia octetul corect per pozitie din
|
||||
`svn cat <fisier>` sau `git cat-file blob <commit>:<cale>`, din Bash, niciodata cu `>` din
|
||||
PowerShell 5.1 (adauga BOM si re-encodeaza). Scrie inapoi ca bytes cp1250 explicit, apoi
|
||||
`txt2vcx.ps1` + fidelity check.
|
||||
|
||||
## Cum se evită pe viitor
|
||||
Cazul simplu (s-a pierdut doar 0xBA), inlocuire punctuala pe octet:
|
||||
|
||||
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).
|
||||
```bash
|
||||
perl -e 'binmode(STDIN);binmode(STDOUT);local $/;$_=<STDIN>;s/\xEF\xBF\xBD/\xBA/g;print' < f > f.tmp
|
||||
```
|
||||
|
||||
Reference in New Issue
Block a user