Az UTF kódolásnak számos előnye van, de vannak negatívumai is, főleg ha összehasonlítás a feladat.
Összehasonlításnál probléma, hogy vannak módosító karakterek, amelyek módosítják az utánuk lévő karakter jelentését. Ez önmagában nem lenne probléma, viszont ez lehetővé teszi azt, hogy ugyan azt a karaktert két különböző bitsorozat írja le.
Egy másik probléma, hogy az UTF karakterek között van több, egymásra kísértetiesen hasonlító karakter. Például a görög kérdőjel ; és a pontosvessző ; megjelenésükben azonos, de két különböző kódpont tartozik hozzájuk. A pontosvessző a \u003B kódpont alatt található, míg az utóbbi a \u037E kódpontot kapta. Ez akár biztonsági kockázat is tud lenni egy weblap esetén, ahol a felhasználók tölthetnek fel adatokat. Egy támadó könnyen kreálhat olyan szöveget, ami átmegy mondjuk egy spam vagy moderációs szűrőn. Egy példa: A Ház és a ℋáz betűtípustól függően akár azonos módon jelenhet meg egy weblapon.
A jó hír azonban, hogy ezekre a problémákra gondoltak a Unicode szabvány létrehozásánál és definiáltak összehasonlítási módokat, amivel megoldhatóak a sorba rendezés problémái, illetve definiáltak normalizálási módszereket, amivel szövegek azonos formára hozhatóak összehasonlítás vagy rendezés előtt.
Ezeket a módokat minden UTF kompatibilis szoftvernek ismernie kell. Ebből adódóan a .NET is rendelkezik ezekkel. A szövegek azonos formára hozásáért a string típus Normalize() metódusa felel. Ebből létezik a paraméter nélküli változat és a paraméteres változat, aminek megadhatjuk a normalizálási formát a NormalizationForm enum egy értékének segítségével.
A Unicode szabvány szabvány 4 normalizálási formát definiál és ezeket írják le a NormalizationForm értékei:
- FormD
A karaktereket kanonikus egyenértékűség bontja és rendezi át. Pl.: â (\u00E2) -> a (\u0061) + ̂ (\u0302)
- FormKD
A karaktereket kanonikus egyenértékűség és kompatibilitás bontja, és rendezi át. Pl.: fi (\uFB01) -> f (\u0066) + i (\u0069)
- FormC
A karaktereket kanonikus egyenértékűség bontja, sorrendbe helyezi, és kanonikus egyenértékűség szerint alkotja újra. Pl.: â (\u00E2) -> a (\u0061) + ̂ (\u0302) -> â (\u00E2)
- FormKC
A karaktereket kanonikus egyenértékűség és kompatibilitás bontja, sorrendbe helyezik és kanonikus egyenértékűség szerint alkotja újra. Pl.: fi (\uFB01) -> f (\u0066) + i (\u0069) -> f (\u0066) + i (\u0069)
Ha paraméter nélkül hívjuk meg a Normalize() metódust, akkor FormC normalizáció fog történni, ami kiszűri a módosító karaktereket, de a vizuálisan és funkcionálisan egyenértékű karaktereket megőrzi. Ez a legtöbb esetben elegendő, de szövegtől és környezettől függően lehet más normalizálásra lesz szükségünk.
A normalizáció után jöhet az összehasonlítás, ami történhet a == operátor segítségével is, de ha kultúrát is figyelembe szeretnénk venni, vagy a kis és nagy betű nem számít, akkor az Equals metódus alkalmazása célszerűbb, mert itt megadhatunk egy StringComparison enum értéket, ami az összehasonlítás működését befolyásolja. A StringComparison lehetséges értékei:
-
CurrentCultureJelenlegi területi és nyelvi beállítások szerint kis és nagybetűt megkülönböztető összehasonlítás.
-
CurrentCultureIgnoreCaseJelenlegi területi és nyelvi beállítások szerint kis és nagybetű között különbséget nem tévő összehasonlítás.
-
InvariantCultureTerületi és nyelvi beállításoktól független kis és nagybetűt megkülönböztető összehasonlítás.
-
InvariantCultureIgnoreCaseTerületi és nyelvi beállításoktól független kis és nagybetű között különbséget nem tévő összehasonlítás.
-
OrdinalBináris, kódpontok szerinti összehasonlítás
-
OrdinalIgnoreCaseBináris, kódpontok szerinti összehasonlítás, kis és nagybetű közötti különbségek figyelmen kívül hagyásával.
Az alábbi program a Normalize és az Equals működését mutatja be:
using System;
internal static class Program
{
private static void Main(string[] args)
{
string pelda = "A̲l̲a̲h̲u̲z̲o̲t̲t̲ ̲t̲e̲s̲z̲t̲ ̲s̲z̲o̲v̲e̲g̲";
Console.WriteLine($"Eredeti: {pelda}");
Console.WriteLine($"Normalized(): {pelda.Normalize()}");
string a = "teszt";
string b = "Teszt";
Console.WriteLine($"a == b: {a == b}");
Console.WriteLine($"a.Equals(b): {a.Equals(b)}");
bool caseIgnored = a.Equals(b, StringComparison.InvariantCultureIgnoreCase);
Console.WriteLine($"caseIgnored: {caseIgnored}");
}
}
A program kimenete:
Eredeti: A̲l̲a̲h̲u̲z̲o̲t̲t̲ ̲t̲e̲s̲z̲t̲ ̲s̲z̲o̲v̲e̲g̲
Normalized(): A_l_a_h_u_z_o_t_t_ _t_e_s_z_t_ _s_z_o_v_e_g_
a == b: False
a.Equals(b): False
caseIgnored: True