Tavarișci soldațî, ia pricazîvaiu vam: ftargaitiși și razgromiți rumînskîi dușmanschi! Asa răsună ordinul comandantului. Ivan deschise aplicația KudaBlyat din dotarea tancului, tastă Bucureşti la destinație, noul Ţarisk ca punct de plecare, la care aplicața răspunse cu Marșrut niet, tavarișci. Ivan scuipă alăturea prin turelă și-nchise ecranul. Privi lung și trist câmpia administrativă din fața lui.

Șirurile de caractere sunt mereu motiv de sărbătoare
La fel se-ntâmplă pentru fiecare din cei trei milioane de luptători îndreptați, într-adevăr, spre plăpânda noastră țărișoară. Blocată într-o buclă pentru depanarea situației, cu ramificații și complicații birocratice, tancurile epuizară motorina stând pe loc, iar invazia se anulă până la noi ordine.
Conaționalii noștri nici măcar nu știau ce fel de glonț le trecuse pe la ureche. Întrebarea rămânea totuși: ce se întâmplase de pieriseră la gunoi trei ani de pregătiri, tone de carburant, munți de hărți și întreaga demnitate cartografică a Imperiului Tovărășesc?
Pe surse am aflat că rușii spărseseră bazele de date românești, stocate la https://strict-secret.romania.ro/sa-nu-vaza-rusii/date-sensibile, protejate, în urma unui audit de securitate, cu noul cont de acces admin și parola valori-europene2026 și le importaseră în sistemele lor. În mod ironic, acestea conțineau și rețeaua detaliată cu orașele rusești.
Acea bază de date, după o investiție de 400 de catralioane de Euro obținuți de la Califatul Uniunii Europene prin programul PNN-CR în schimbul instituirii regulii ca sistemele de încălzire și climatizare să fie folosite respectând paritatea zi-etaj, suferiseră o actualizare majoră, cu două direcții fundamentale de dinamică interoperabilitară:
– diacriticele au fost convertite la codurile canonice;
– denumirea bazei de date a fost schimbată din baza-de-date.DBF la baza-de-date-standard-european.DBF.
Rușii, pe de altă parte, foloseau pe tancuri un sistem de operare mai vechi, Tavarișci 2000, unde tastatura folosea ş-urile și ţ-urile cu sedilă. Pentru ochiul omenesc, diferența era aproape invizibilă. Pentru calculator, însă, a fost suficientă cât să oprească o invazie.
Iată, deci, încrederea în progresul tehnologic nu a fost nefondată. Mai jos aveți diferențele dintre caractere într-un font ce le face foarte vizibile. Am omis majusculele, ar fi ocupat loc de mântuială, diferența fiind aceeași.
| Canonice (cu virgulă) | Belele (cu sedilă) |
|---|---|
![]() | ![]() |
![]() | ![]() |
Desigur, nu este nevoie să fii invadator rus ca să ai de-a face cu această gingașă problemă, ci doar să ai un strop de ghinion și să fii un pic puturos. Primind pe e-mail un clasic Nu merge și un fișier CSV, cu aluzia că a-ncercat să-l importe și nu a reușit, apoi încă unul, la interpelarea mea, cu noi detalii prețioase – Nu știu, am încercat și nu merge, m-am găsit și eu, după inspectarea fișierului, în aceeași mocirlă care scufundase Țariskul și salvase biata Românie.
S-ar putea spune, filozofând sumar, că limba noastră este, pe bună dreptate, o comoară în adâncuri înfundată. Ce nu se spune e că-i vorba de adâncurile code-point-urilor, dintr-unul din acele motive ce, în limbaj de specialitate, se numesc istorice și relevă una din tragediile destinului românesc: se zbate între turci, ruși și, mai noi, o înfiorătoare birocrație bruxelleză mai rea decât amândouă la un loc.
Impostorii cu sedilă – U+015F și U+0163, sunt, de fapt litere mai degrabă specifice sistemelor turcești (cel puțin ş-ul e). În 1987, româna a fost legată de un standard internațional, ISO 8859-2, care stipula – din motive de economie de caractere și de neatenție – caracterele cu sedilă.
Când a fost compus Unicode, câțiva ani mai târziu, a moștenit codificarea fără să clipească. Românii au observat, poate chiar s-au bășicat, și-au trimis o plângere oficială la ISO. În 1999, Unicode 3.0 a adoptat în sfârșit formele canonice, sub Latin Extended B – Additions for Romanian.
Dacă a existat sau nu o notă blândă care să spuie că sedila poate fi folosită ca substitut nu știu, bref e că din când în când mai apar. Și-au apărut și-n acel fișier CSV. În baza de date erau codificate corect, deci nu au putut fi stabilite corespondențe și, ce să vezi, chiar n-a mers. Răzbunare pentru căderea Plevnei.
ISO/IEC 8859-16:2001 uses S and T with comma below (Unicode 0218-021B), whereas ISO/IEC 6937:2001 uses S and T with cedilla below (Unicode 015E, 015F, 0162, 0163). The problem is solved with the help of a compromise, in that the ISO/IEC 6937:2001 revision has an additional note (Note 2, bottom of Table 4), that states something like „subject to the agreement of originator and receiver in information interchange, the letters WITH CEDILLA may be used to substitute for the letters WITH COMMA BELOW”.

Diferențele dintre caractere, în VS2026 la 200% zoom; secvență de cod pentru testare.
Pe vremuri, la îndemnul unui Programator-Tătic, un fel de Yoda, am întreținut o serie de utilitare ce mențineau o cartografiere meticuloasă între caractere pentru a putea servi unei funcții ajutătoare de înlocuire a diacriticelor cu caractere normale ASCII, tocmai din cauza acestor gen de belele cu texte digitale – Character mapping maintain you must!
Din păcate, nu prea am mai întreținut acest bun obicei, iar de la o vreme folosesc în C# Diacritics.NET. După ce am izolat textele problemă, am scris câteva teste rapid și am observat că sedilele rămâneau. Inspectând codul sursă, folosește, într-adevăr, tot metoda cartografierii prin dicționare, iar cel românesc este:
{ 'Â', "A" },
{ 'Î', "I" },
{ 'â', "a" },
{ 'î', "i" },
{ 'Ă', "A" },
{ 'ă', "a" },
{ 'Ș', "S" },
{ 'ș', "s" },
{ 'Ț', "T" },
{ 'ț', "t" }
Adică doar variantele oficiale – U+0219 (ș), U+0218 (Ș), U+021B (ț), U+021A (Ț) – scăpând pe la garnitura de chiulasă caracterele cu sedilă. Mă grăbesc să adaug că nu diacritic abordarea pe bază de dicționar, întrucât rezolvă câteva probleme pe care soluția adoptată de mine (de natură complementară, până la urmă) nu poate fi rezolvată decât așa.
public static class StringExtensions
{
public static string RemoveDiacriticsSafe(
this string value,
Func<string, string> postProcess = null
)
{
if (string.IsNullOrEmpty( value ))
return value;
// Generic diacritics removal
string formD = value.Normalize( NormalizationForm.FormD );
StringBuilder sb = new StringBuilder( formD.Length );
foreach (char ch in formD)
{
UnicodeCategory cat = CharUnicodeInfo.GetUnicodeCategory( ch );
if (cat != UnicodeCategory.NonSpacingMark &&
cat != UnicodeCategory.SpacingCombiningMark &&
cat != UnicodeCategory.EnclosingMark)
{
sb.Append( ch );
}
}
string replaced = sb.ToString()
.Normalize( NormalizationForm.FormC );
return postProcess != null
? postProcess(replaced)
: replaced;
}
}
Ce se întâmplă este că normalizez șirul de caracter în forma normală D, colectez doar caracterele de bază și apoi recompun șirul în forma normală C. Formele acestea normale, poate am pus carul între boi și ar fi trebuit să explic un pic, sunt modalități de reprezentare a caracterelor:
– forma normală C combină caracterul de bază și accentul său într-un singur caracter precompus, atomic, bunăoară Ș = U+0218;
– forma normală D le menține separat, acolo unde se poate și nu se poate peste tot, dar pentru exemplul nostru Ș se poate (inclusiv pentru cel nelegiuit): Deci Ș = U+0218 în FormC este S + U+0326 (combining comma below) în FormD.

FormC vs FormD
Nu toate caracterele ce fac scandal pot fi descompuse, iar insectarul limbilor europene, o familie extinsă și nu mereu funcțională, ne furnizează câteva exemple: ß, æ, Ø, Ł. Germana, mă scuzați pentru înjurătură, ne dă măcar ß, iar restul vin de prin vecini, ca să nu zicem că numai unii sunt de vină pentru suferințele inginerilor.
Deci, dacă ne bazăm doar pe formele normale, situația ar fi la fel de ߯Łită. Acum socot că se-nțelege de ce am zis că nu critic abordarea: pur și simplu trebuie stabilită o legătură între ele și cartografierea e cea mai simplă.
Din fericire, C-ul cu grătare preia șirul de caractere exact așa cum i-a fost înmânat – fie FormC, fie FormD. Din și mai multă fericire, convertirea din FormC–FormC și din FormD–FormD este o funcție identică, iar utilitarul de mai sus va funcționa bine-mersi:
[Test]
public void Test_PreserveFormDInlineInput()
{
//Written explicitly in Form D (S + combining comma below)
string inlineFormD = "BUCURES\u0326TI";
//C# did not normalize this inline string to Form C.
bool isFormC = inlineFormD.IsNormalized();
Assert.That( isFormC, Is.False );
//But I CAN normalize that to FormC
// and then move on with my life
string asFormC = inlineFormD.Normalize( NormalizationForm
.FormC );
Assert.That( "BUCUREȘTI",
Is.EqualTo( asFormC ) );
//And I can also normalize it to FormD, which is a no-op
string asFormD = inlineFormD.Normalize( NormalizationForm
.FormD );
Assert.That( "BUCURES\u0326TI",
Is.EqualTo(asFormD));
}
[Test]
public void Test_PreserveFormCInlineInput()
{
//Written explicitly in Form C
string inlineFormC = "BUCUREȘTI";
//C# did not normalize this inline string to Form C.
bool isFormC = inlineFormC.IsNormalized();
Assert.That( isFormC, Is.True );
//Normalizing a Form C string to Form C is a no-op,
// but it should still work
string asFormC = inlineFormC.Normalize( NormalizationForm
.FormC );
Assert.That( "BUCUREȘTI",
Is.EqualTo( asFormC ) );
}
[Test]
public void Test_CanRemoveFromFormD()
{
string inlineFormD = "BUCURES\u0326TI";
string clean = inlineFormD.RemoveDiacriticsSafe();
//The result should be the same
// as if we had started with Form C
Assert.That( clean,
Is.EqualTo( "BUCURESTI" ) );
}
[Test]
public void Test_CanRemoveFromFormC()
{
string inlineFormC = "BUCUREȘTI";
string clean = inlineFormC.RemoveDiacriticsSafe();
//The result should be the same
// as if we had started with Form D
Assert.That( clean,
Is.EqualTo( "BUCURESTI" ) );
}
În fine, odată obținută FormD, putem spicui caracterele mai năroade după categoria lor Unicode. Punctual, ne dorim să ne lepădăm de:
| La gunoi | Valoare Enum C# | Exemplu |
|---|---|---|
| Semn diacritic care se atașează peste/sub literă și nu ocupă spațiu separat. | NonSpacingMark | Accent, virgulă dedesubt U+0326, sedilă combinatorie U+0327 |
| Semn combinatoriu care nu influențează litera originală precum un accent, dar consumă spațiu vizibil pe orizontală și se combină, să zicem, logic pentru a forma un simbol distinct. | SpacingCombiningMark | Frecvent în scrieri indiene; irelevant pentru Română. |
| Semn care îmbrățisează alt caracter. | EnclosingMark | Cercuri, pătrate sau marcaje puse peste caracter. Ex. E cu pătratul U+20DE: E⃞ |
Iar odată pusă-n opera mica metodă, putem complementa rutina oferită de Diacritics.NET. Mai întâi lăsăm normalizarea Unicode să curețe ce poate curăța generic. Apoi, pentru caracterele care nu se descompun frumos în literă de bază și semn diacritic, rămâne dicționarul explicit, această agendă telefonică a istoriei tipografice.
Ceea ce, dacă rușii ar fi făcut, acum cărțile de istorie ar fi vorbit despre înlocuirea dictaturii de culoare albastră cu steluțe galbene de cea roșie. Harașo, Tavarișci 2000, mulțumită ție lumea liberă se poate minți în continuare că e liberă!



