Ékezetes FAQ webművészek számára (Nem mennek az ékezetek a szájtomon!)

K: Nem mennek az ékezetek a szájtomon, pedig már összevissza kapkodtam, mindent átállítottam és vissza, mit csináljak még?!
V: Végső esetben próbálj meg tájékozódni, valamilyen kódkészletet kiválasztani, és minden beállításnál azt használni.

K: Mi az a 'minden beállítás'?
V: Először jöjjön a HTTP-header, azon belül is a Content-Type, pédául ennek az oldalnak az esetében:
Content-Type: text/html; charset=iso-8859-2
K: Na ez honnan jön?
V: Statikus fájlok (HTML, CSS, JS stb) esetén a .htaccess-ben az AddDefaultCharset-ből (ha az nincs, akkor az apache globális beállítása érvényesül), dinamikus fájlok esetén ezt felülbírálhatjuk úgy, ha magunk állítjuk elő ezt a fejrész sort. Példa PHP-ban:
header ('Content-Type: text/html; charset=iso-8859-2');
Példa Perl CGI-ben:
print "Content-Type: text/html; charset=windows-1250\r\n";
K: PHP-ben mi lesz az alapértelmezés, ha nem használok header-t?
V: A php.ini-ből veszi az alapértelmezett értéket, pl:
default_mimetype = "text/html"
default_charset = "iso-8859-2"

K: Kipróbáltam, de nem ment!
V: Minden más kiíratás előtt kell lennie; PHP-ben az is gondot okoz, ha a <?php tag nem a fájl legelején van, hanem van előtte egy üres sor, szóköz, tabulátor, BOM; CGI esetén pedig az okozhatja a gondot, ha elfelejted a fejrészt lezáró üres sort kiadni, hiszen ekkor azt hiszi az Apache, hogy ez is az adathoz tartozik.

K: Mi az a BOM?!
V: Egy hárombájtos szekvencia a fájl elején, ami azt jelezné, hogy a fájl UTF-8 kódolású. Tudnod kell róla, hogy UNIX-ban nem használatos, csak Windows-ban; továbbá, hogy HTML, PHP, CGI stb. fájlban nincs rá szükség, szabadulj meg tőle! Bővebben itt: wiki:BOM
K: Szóval ilyenkor ne legyen BOM a fájl elején. Van olyan eset, amikor viszont érdemes BOM-ot használni?
V: Ha a programodból (PHP, CGI) olyan UTF-8 kódolású szöveges fájlt állítasz elő (pl .TXT, .CVS), amelyet a felhasználó külső programmal nyit meg (Notepad, Excel), akkor ezzel jelezheted az UTF-8 kódolást. PHP példa:
print "\xEF\xBB\xBF";

K: És hogyan lehet a HTTP-fejrészt ellenőrizni?
V: Például a wget -S opciójával, vagy a Firefox LiveHttpHeaders kiegészítőjével.

K: Nem veszi figyelembe a .htaccess-t, mit csináljak?
V: Lehet, hogy nem Apache-t használnak, hanem valamilyen egzotikus webszervert, amelyik nem ismeri ezt az opciót, vagy átnevezték a .htaccess-t (AccessFileName opció), vagy pedig a globális beállításban megtiltották, hogy a .htaccess-ből vegye ezt az opciót. Mindezen esetekben a szolgáltatóval kell egyeztetned, a legutóbbi esetben például ezt kell magadniuk:
AllowOverride FileInfo
K: Mi legyen a következő lépés?
V: A HTML fejrészben (HEAD) is meg lehet adni (értsd: érdemes megadni) a kódolást, valahogy így:
<META http-equiv="Content-Type" content="text/html; charset=iso-8859-2">

K: De minek, ha ennek nincs prioritása a HTTP-fejrész fölött?
V: Valóban nincs, de a HTTP-fejrésszel szemben ez akkor is megmarad, ha az oldalt elmented fájlba, és később megtekinted/módosítod.

K: Ennek van értelme, de mi a helyzet a JS, CSS stb. fájlokkal?
V: Javascript-re nem tudom a választ, CSS esetén a @charset használható:
@charset "ISO-8859-2";
XML esetén a fejrész tartalmazza a kódkészletet, pl.:
<?xml version="1.0" encoding="iso-8859-2"?>
K: Magában a HTML-ben is meg lehet adni a kívülről beemelt javascript vagy stíluslap kódolását?
V: Hát például így:
<SCRIPT src="neve.js" type="text/javascript" charset="UTF-8">
<LINK href="neve.css" rel="stylesheet" type="text/css" charset="UTF-8">
Sőt, adott esetben az <A> tagban is megadhatjuk a linkelt oldal kódolását:
<A src="neve.txt" type="text/plain" charset="windows-1250">
K: Mi történik, ha az URL 'path' részében van ékezetes betű?
V:A HTTP-üzenetben %-szekvenciaként fog közlekedni az illető karakter kódja, de persze az nem garantált, hogy a szerver és a kliens ugyanazt a kódolást használják. Például a magyar wikipedia az utf8-at peferálja, de a latin2-t is elfogadja (illetve átirányítással kezeli)
kérés:
GET /wiki/R%E9pa HTTP/1.1

válasz:
HTTP/1.1 301 Moved Permanently
Location: https://hu.wikipedia.org/wiki/R%C3%A9pa

kérés:
GET /wiki/R%C3%A9pa HTTP/1.1

válasz:
HTTP/1.1 200 OK
K: És ha a 'domain' részben van ékezet, mint például http://szűrőgép.hu/?
V: A printable unicode avagy punycode nevű kódolást kell alkalmazni, és az xn-- prefixet tenni elé. Példa (az idn segédprogramot használva):
$ idn szűrőgép.hu
xn--szrgp-esa04e4h.hu
Lásd még az rfc-3490-et és a libidn könyvtárat.
K: Tehát ha a 'domain' részben van ékezet, akkor szép magyaros szöveget láthatnak a felhasználók a címsorban? Tök jó!
V: Csak amíg ki nem javítják a böngészőket, ugyanis ez átverős oldalak készítésére is alkalmas (lenne). Lásd ezeket:
Wordfence: Chrome and Firefox Phishing Attack Uses Domains Identical to Known Safe Sites
Index Tech: Cirill betűkkel hamisítják a weboldalakat
www.xn--e1awd7f.com
K: Ebből mi az utolsó link? Ha rákattintok, megsemmisül a gépem?
V: Nem, nem történik semmi, csak a böngésző címsorát kell megnézned: ha az jelenik meg, hogy www.еріс.com, akkor a böngésződ támadható ilyen módon. (Ellenőrzésképpen vágd ki innen ezt a négy betűt: еріс és másold át egy hexa-kód megmutatásra képes szövegszerkesztőbe.)
— Kódkészletek —

K: Jó választás az iso-8859-1 (avagy latin1)?
V: Nem, mert nincs benne őŐűŰ csak õÕûÛ.
K: Dehát sok helyen az az alapértelmezés!
V: És mégsem jó a magyar betűkhöz. Válassz inkább ezekből: iso-8859-2 (latin2), win-1250, utf-8.

K: És az igaz, hogy a latin2 (más néven iso-8859-2) ugyanaz, mint a win-1250?
V: Nem egészen. A magyar betűk ugyanott vannak mindkettőben, ez igaz; viszont a win-1250-ben van pár hasznos jel, ami a latin2-ből hiányzik, például az euro jele (0x80 €), a hárompont (0x85 …), az ndash (0x96 –), az mdash (0x97 —) és a kopirájt jele (0xA9 ©). A pontos lista unicode.org-on: 8859-2.TXT cp1250.TXT

K: És a csehszlovák/szerbhorvát betűk is ugyanott vannak a fenti két kódolásban?
V: Sajnos nem, például az © latin2-ben 0xA9, win1250-ben 0x8A. (Újabb ok az utf8 használatára!)

K: Igaz, hogy a legtöbb baj az Ő és az Ű betűvel van? És miért?
V: Igaz, mégpedig azért, mert csak ezek nincsenek benne a latin1-ben (ami a legtöbb esetben az alapértelmezés), hanem helyettük Õ és Û van azon a kódon.

K: Azt hallottam, hogy ha azt írom helyettük, hogy &Otilde; és &Ucirc; akkor a fájl kódolásától függetlenül mindig jó lesz.
V: Ez majdnem igaz, a pontos információ az, hogy így a fájl kódolásától függetlenül mindig rossz lesz. Ha &-szekvenciákat akarsz, akkor használd ezeket: &#x150;=Ő, &#x151;=ő, &#x170;=Ű, &#x171;=ű.

K: Hol találok egy listát az összes ilyen &-szekvenciáról, hogy pl.: &hellip;=… vagy &beta;=β?
V: Mondjuk a w3.org-on: Character entity references in HTML 4. Természetesen a kódokat is használhatod, decimálisan vagy hexásan, pl.: &#915;=&#x393;=&Gamma;=Γ.

K: Kódolásra visszatérve, lehet, hogy érdemes lenne áttérnem az utf8-ra?
V: Nem lehetetlen, különösen, ha akár csak a távlati terveid között is szerepel orosz, ázsiai, eszperantó vagy klingon karakterek ábrázolása.

K: Már elkezdtem valamit alkotni, de most szeretném átkonvertálni egy másik kódkészletbe, mit tegyek?
V: Könnyen lehet, hogy a kedvenc szövegszerkesztőd is képes erre (keresgélj a 'Save As' menüpontnál), de ha nem, használj valami olyasféle programot, mint az iconv! (Még egyszer figyelmeztetlek: az egyetlen dolog, amit a 'BOM'-ról tudnod kell, az az, hogy nincs rá szükséged!)

K: Mit kell tudnom az UTF-7-ről?
V:Valószínűleg semmit, de egyszer kipróbálhatod (a -f (=from) opciót igazítsd a saját aktuális beállításodhoz):
echo 'árvíztűrő tükörfúrógép' | iconv -f latin2 -t utf-7
+AOE-rv+AO0-zt+AXE-r+AVE t+APw-k+APY-rf+APo-r+APM-g+AOk-p
Megj: További kódolások, amikről nem kell tudnod: UTF-1, UTF-5, UTF-6, UTF-9, UTF-18.
K: Ha már furcsa kódolásokról beszélünk, a cat -v milyen jeleket használ?
V Két speciális jelölést használ, az egyik a ^kalap karakter, a másik a M- prefix. Egy táblázattal próbálom szemlélteni a működését:
ezt látjuk		ezt jelenti (karakterkód hexásan) megjegyzés
^A .. ^Z 		00..1A				  0x40-et levonunk a karakterkódból
^[ ^\ ^] ^^ ^_		1B..1F				  0x40-et levonunk a karakterkódból
normál ASCII karakter	20..7E
^?			7F				  egyedik jelölés
M-^A .. M-^Z		80..9A				  0x40-et hozzáadunk a karakterkódhoz
M-^[ .. M-^_		9B..9F				  0x40-et hozzáadunk a karakterkódhoz
M-normál ASCII karakter	A0..FE				  0x80-at hozzáadunk a karakterkódhoz
M-^?			FF				  egyedik jelölés
K: Ha nekem jó lenne az ISO-8859-2, de euró-jelet (€) is szeretnék, akkor használhatom az ISO-8859-16-ot?
V: Használhatod, de az űŰ nem ugyanott van a kettőben, vagyis kovertálni kell... akkor már jobban jársz a windows-1250-nel (abban a magyar betűk ugyanott vannak), vagy az utf-8-cal (univerzális, korszerű megoldás).
K: Tulajdonképpen mit szabványosít az Unicode?
V: Karakterekhez rendel számokat. Azt nem írja elő, hogy ezeket a számokat hogyan kell memóriában vagy fájlban tárolni.
K: De az legalább egy remek dolog, hogy ezek a számok sosem változnak!
V: Tulajdonképpen egyes tibeti és koreai karakterekkel azért történt ilyesmi, pl.:
    1.0  1.1  2.0
 ཀ 1000    –  F40
가 3400 3400 AC00
K: Nem az lenne a legjobb, ha egyszerűen Unicode-ot használnánk? Ugyebár minden karakterhez hozzárendeltek egy 0 és 65535 közötti (tehát két bájton tárolható) számot...
V: Az a szám már messze túl van a 65535-ön, például az &#127828;=&#x1f354;=🍔 egy hamburger. UTF-8-ban a kódja: f0 9f 8d 94 (ha a böngésződ nem talál olyan fontot, amiben benne van, akkor persze kérdőjelet/furcsa izét látsz). De még ha bele is férne két bájtba, akkor is két lehetőség lenne a tárolásra, a little-endian, meg a big-endian, szóval eleve nem lenne univerzális megoldás.
K: Franc. Akkor ezt nem is használják?
V: De igen, úgy hívják őket, hogy UCS-2LE és UCS-2BE. Minta:
$ echo -n 'tűrő' | iconv -f latin2 -t UCS-2LE | od -tx1
0000000  74 00 71 01 72 00 51 01
$ echo -n 'tűrő' | iconv -f latin2 -t UCS-2BE | od -tx1
0000000  00 74 01 71 00 72 01 51
K: Ezek közül valamelyik megegyezik az UTF-16-tal?
V: Az UTF16-LE és az UTF16-BE ezeknek továbbfejlesztett változatai, képesek minden unikódot ábrázolni, igaz nem fix hosszon. Az előbbi hamburgerrel kipróbálva:
$ echo -n $'\xf0\x9f\x8d\x94' | iconv -f utf-8 -t utf-16le | od -tx1
0000000 3c d8 54 df
$ echo -n $'\xf0\x9f\x8d\x94' | iconv -f utf-8 -t utf-16be | od -tx1
0000000 d8 3c df 54
K: Hogyan is jön ez ki?
V: Ezt a dolgot helyettesítő pár-nak hívják (surrogate pair); a pár két fele 1024 érték közül tárol egyet-egyet (az első a d800-dbff tartományból, a második a dc00-dfff tartományból vesz fel értéket), így összesen 1048576 kódot lehet így tárolni. Valahogy így tudnám ezt táblázattal szemlélteni (minden kód hexadecimális, az unikód elé U+ -t írok, hogy gyakoroljuk ezt a jelölést, továbbá az feltüntetem az UTF8-as és a CESU8-beli kódot is):
  Unikód   UTF-16     UTF-8        CESU-8
     U+0   0000       00           00
  U+D7FF   D7FF       ED-9F-BF	   ED-9F-BF
-- itt 2048 érték kimarad, nem reprezentál karaktert --
  U+E000   E000       EE-80-80     EE-80-80
  U+FFFF   FFFF       EF-BF-BF     EF-BF-BF
 U+10000   D800 DC00  F0-90-80-80  ED-A0-80 ED-B0-80
 U+103FF   D800 DFFF  F0-90-8F-BF  ED-A0-80 ED-BF-BF
U+10FC00   DBFF DC00  F4-8F-B0-80  ED-AF-BF ED-B0-80
U+10FFFF   DBFF DFFF  F4-8F-BF-BF  ED-AF-BF ED-BF-BF
K: Igazából nem U+xxxx illete U-xxxxxxxx a hivatalos jelölés (vagyis négy illetve nyolc hexa számjegy)?
V: De igen, ha programokkal kommunikálsz, használd így; ha viszont emberekkel, akkor talán elvárhatsz némi rugalmasságot az olvasótól.
K: Például hol használnak ilyen UTF-16-ot, és miért?
V: Például MS Windows, Oracle, Java, AIX... És azért, mert volt idő, amikor még úgy tűnt, hogy 16 biten el fog férni az összes unikód (bár ezt egy kicsit nehéz elhinni, ha pl. a kínai szóírásra gondolunk), utána viszont már késő volt teljes átállásra, tehát valami kvázi-kompatibilis megoldást kellett tákolni, ez lett az UTF-16. Azok a programok/platformok, amik később tértek át az unikódra, rendszerint az UTF-8-at használják.
K: A kétféle UTF-16 után azt tippelem, hogy UTF-32-ből is kettő van: UTF32-LE és UTF32-BE...
V: Stimmel. A jó hír viszont az, hogy ez legalább fixhosszú kódokból áll. A rossz pedig az, hogy nagyon sok a kihasználatlan (nulla értékű) bit.
K: És van valami módszer, amivel ezt a sok szép lehetőséget azonosítani lehet?
V: Ez lenne a BOM jelentősége, rögtön a fájl legelején, az alábbiak szerint:
EF-BB-BF    UTF-8
FF-FE	    UTF-16LE
FE-FF	    UTF-16BE
FF-FE-00-00 UTF-32LE
00-00-FE-FF UTF-32BE
K: Ebben a FAQ-ban sok helyen van szó az UTF-8-ról, összefoglalhatnánk itt a legfontosabb tudnivalókat?
V: Na nézzük listában:
Egy kis ábra az egyes szekvenciákról:
bájtok száma:  1	      2		     3		    4
hasznos bitek: 7	      11	     16		    21
első bájt:     00..7f	      c2..df c0..df  e0..ef	    f0..f4   f0..f7
minimum:       00	      c2 80	     e0 a0 80	    f0 90 80 80
maximum:       7f	      df bf	     ef bf bf	    f4 8f bf bf
U+minimum:     U+0	      U+80	     U+800	    U+10000
U+maximum:     U+7f	      U+7ff	     U+ffff	    U+10ffff U+1fffff
	      +-----------+  +-----------+  +-----------+  +-----------+ 
	      | 0xxx xxxx |  | 110x xxxx |  | 1110 xxxx |  | 1111 0xxx |
	      +-----------+  +-----------+  +-----------+  +-----------+
			     | 10xx xxxx |  | 10xx xxxx |  | 10xx xxxx |
			     +-----------+  +-----------+  +-----------+
					    | 10xx xxxx |  | 10xx xxxx |
					    +-----------+  +-----------+
							   | 10xx xxxx |
							   +-----------+
Az érdekesség kedvéért egy ábra arról, hogy mi volt korábban, amikor még 5-6 bájtos szekvenciák is lehettek:
bájtok száma:  1	      2		     3		    4		   5		  6
hasznos bitek: 7	      11	     16		    21		   26		  31
első bájt:     00..7f	      c2..df c0..df  e0..ef	    f0..f7	   f8..fb	  fc..fd
minimum:       00	      c2 80	     e0 a0 80	    f0 90 80 80    f8 88 80*3	  fc 84 80*4
maximum:       7f	      df bf	     ef bf bf	    f7 bf bf bf    fb bf bf*3	  fd bf bf*4
U+minimum:     U+0	      U+80	     U+800	    U+10000        V+200000       V+4000000
U+maximum:     U+7f	      U+7ff	     U+ffff	    V+1fffff	   V+3ffffff	  V+7fffffff
	      +-----------+  +-----------+  +-----------+  +-----------+  +-----------+  +-----------+ 
	      | 0xxx xxxx |  | 110x xxxx |  | 1110 xxxx |  | 1111 0xxx |  | 1111 10xx |  | 1111 110x |
	      +-----------+  +-----------+  +-----------+  +-----------+  +-----------+  +-----------+
			     | 10xx xxxx |  | 10xx xxxx |  | 10xx xxxx |  | 10xx xxxx |  | 10xx xxxx |
			     +-----------+  +-----------+  +-----------+  +-----------+  +-----------+
					    | 10xx xxxx |  | 10xx xxxx |  | 10xx xxxx |  | 10xx xxxx |
					    +-----------+  +-----------+  +-----------+  +-----------+
							   | 10xx xxxx |  | 10xx xxxx |  | 10xx xxxx |
							   +-----------+  +-----------+  +-----------+
									  | 10xx xxxx |  | 10xx xxxx |
									  +-----------+	 +-----------+
											 | 10xx xxxx |
											 +-----------+
— C-programozás —
K: Ha én mondjuk C-programozó vagyok, akkor a wchar_t mit fog jelenteni az UCS-*, UTF-16*, UTF-32* lehetőségek közül?
V: Platformfüggő.
K: Hogy mi van?!
V: Tudom, hogy ez most úgy hangzik, mintha nem lehetne portábilisan használni, de a valóság az, hogy nem lehet portábilisen használni. Tehát ha mondjuk az a vágyad, hogy írj egy platformfüggetlen programot, ami előállít egy fájlt pl. UTF-16LE/-16BE/-32LE/-32BE kódolásban, akkor abban a wchar_t egycsapásra nem segít semmit. Tesztprogram:
/* wchartest.c */

#include <stdio.h>
#include <wchar.h>

int main (void)
{
    wchar_t wstring [] = { 0xf6, 0x151, 0xfc, 0x171 /* o: o" u: u" */ };
    size_t i;

    for (i=0; i<sizeof(wstring); ++i) {
        unsigned c= ((unsigned char *)wstring)[i];
        if (i!=0 && (i%sizeof(wchar_t)==0)) printf ("-");
        printf ("%02x", c);
    }
    printf ("\n");
    return 0;
}
Ennek a kimenete különféle platformokon:
linux, x86:      f6000000-51010000-fc000000-71010000
Windows, x86:    f600-5101-fc00-7101
AIX, PowerPC/32: 00f6-0151-00fc-0171
AIX, PowerPC/64: 000000f6-00000151-000000fc-00000171
K: Na jó, de én nem is így akarom a programban rögzíteni a stringeket, hanem L'ä' és L"öőüű" formában. Ebben van valamilyen problémás rész vagy hibalehetőség?
V: Annyi, hogy a compiler-nek tudnia kell, hogy milyen kódolásban van a forrásprogram. Hogy ez hogyan történik, az platformfüggő lehet; ha nem adod meg, akkor valamiféle alapértelmezés fog érvényesülni. Például gcc esetén az -finput-charset való erre, pl (linux).:
# a source latin2-ben van
$ gcc -finput-charset=iso-8859-2 -o wchartest_l wchartest_l.c
$ ./wchartest_l
f6000000-51010000-fc000000-71010000-00000000

# a source utf8-ban van
$ gcc -finput-charset=utf-8 -o wchartest_u wchartest_u.c
f6000000-51010000-fc000000-71010000-00000000
K: Ha eltévesztem, akkor fordítási hibát kapok, vagy a futás lesz rossz?
V: Igen. (A fordító észreveheti, hogy a latin2 (általában) nem valid utf8, de a fordítva ez nem igaz.)
K: MS Windows esetén micsoda a wchar_t?
V: Intel platformon 16-bites little endian, a 16-biten el nem férő karaktereket két egymást követő wchar_t tárolja (lásd: UTF-16LE, surrogate pairs).
K: És igaz, hogy majdnem a teljes Windows API-t 'megduplázták', minden hagyományos stringeket használó függvénynek lett egy párja, ami UTF-16-os ("széles" avagy "wide") stringeket használ?
V: Igaz (pl. a CreateWindow makró a CreateWindowA és CreateWindowW függvények valamelyikévé fog kifejtődni), de lehetséges olyan programot írni, ami egy #define-tól (UNICODE) függően lesz "keskeny-" vagy "széles-" karakteres.
TCHAR name= _T("Őrült Űrhajós");
size_t namelen= _tcslen(name);
K: És milyen függvényekkel konvertálhatok Windows-ban "keskeny" és "széles" között?
V: Ezeket használhatod: MultiByteToWideChar WideCharToMultiByte. Nevükkel ellentétben nem csak multibyte-ról/-ra tudnak konvertálni (lásd itt: Code Page Identifiers, valamint használhatsz default ansi kód és default oem kód értékeket is (CP_ACP és CP_OEMCP), ha platformfüggő működést akarsz elérni).
K: C-programban milyen oktális/hexa szekvenciákat használhatok karakter- és stringliterálokban?
V: Az egyre újabb szabványok egyre újabb lehetőségeket hoztak:
\000	    --  egy-három oktális számjegy a 000-377 tartományból
\x00	    --  egy vagy több hexadecimális számjegy (a hossz nincs korlátozva)
\u0000      --  négy hexadecimális számjegy (unikód)
\U00000000  --  nyolc hexadecimális számjegy (unikód)
Megjegyzések: Az persze lehet, hogy nem minden lehetőség használható minden platformon, illetve hogy valamilyen compiler-opció kell hozzá (pl. gcc esetén a -std=c99).
A \x szekvenciánál a hossz nincs korlátozva, de ha a megadott érték nem fér el a használt karaktertípusban (char, wchar_t, char16_t, char32_t), akkor az eredmény platformfüggő (pl hibaüzenet, az érték csonkítása, stb).
A \u és \U szekvenciáknál az érték az adott karaktertípusnak megfelelően konvertálódik UTF16-re, UTF32-re vagy UTF8-ra; \x szekvenciáknál nincs konverzió.
K: Mit kell tudnom a C11-es szabvány szerinti új char16_t és char32_t típusokról?
V: Ezek az uchar.h-ban definiált előjel nélküli integer típusok, amelyek 16- illetve 32-bitesek. Legalább.
K: Legalább?! Ugye jobb lesz nekem, ha nem gondolok bele abba, hogy mit jelent ez a legalább?
V: Határozottan. Nézzük meg inkább, hogyan tudsz literálokat definiálni ezekhez a típusokhoz:
    wchar_t  cw=    L'ő', sw[]=  L"tűzvész"; /* platformfüggő */
    char16_t c16=   u'ő', s16[]= u"tűzvész"; /* UTF-16 */
    char32_t c32=   U'ő', s32[]= U"tűzvész"; /* UTF-32 */
    char                  su8[]= u8"tűzvész";/* UTF-8 */
K: Az utolsó az UTF-8, ugye? De azt minden további nélkül tudok használni, nemde?
V: Igen, ha a forrásprogram UTF-8-ban van. Az u8-nál viszont a fordítóprogram konvertál UTF-8-ra. Ehhez persze tudnia kell, hogy milyen karakterkészetetben van a forrásprogram. Ha ez az információ hiányzik vagy hibás, akkor fordítási hiba és/vagy hibás működés lesz belőle (lásd fentebb). Persze ugyanez igaz a L/u/U literálokra is.
K: gcc-nél említettük a -finput-charset opciót. Ez csak az L/u/U/u8 típusú literálokra vonatkozik, a hagyományosakra nem?
V: Hagyományos észjárással így képzelnénk, de igazából a gcc a "sima" string- és karakterliterálokat is konvertálja "input-charset"-ről UTF-8-ra.
Szerencsére rá lehet venni, hogy ezt visszacsinálja, ha megadjuk a -fexec-charset opciót, ugyanazzal az értékkel,
pl. ha a fenti stringeket teszteljük egy kis programmal (amit latin2-ben rögzítünk):
gcc -std=c11 -finput-charset=ISO-8859-2 -fexec-charset=ISO-8859-2 tuzvesz.c -o tuzvesz
./tuzvesz
"sima" string:      74 fb 7a 76 e9 73 7a
wchar_t string:     74 00 00 00 71 01 00 00 7a 00 00 00 76 00 00 00 e9 00 00 00 73 00 00 00 7a 00 00 00
char16_t u"string": 74 00 71 01 7a 00 76 00 e9 00 73 00 7a 00
char32_t U"string": 74 00 00 00 71 01 00 00 7a 00 00 00 76 00 00 00 e9 00 00 00 73 00 00 00 7a 00 00 00
utf8 u8"string":    74 c5 b1 7a 76 c3 a9 73 7a
Megjegyzés: Ne felejtsük el, hogy a byte-order és a wchar_t mérete platformfüggő.
K: Szóval a gcc-ben -finput-charset opciót használhatom. És clang esetén?
V: Az opció ugyanaz, de a használható értékkészlet egy kicsit szűkebb: csak az UTF-8 értéket választhatod.
K: Nem baj, az iconv segít, ugye?
V: Persze. Feltéve, hogy csak L"", U"", u"", u8"" formájú stringek és karakter-konstansok vannak a programban, mert ha hagyományos stringek (karakterek) is vannak benne, akkor a program mást fog csinálni, mint a konverzió előtt (már ha egyáltalán lefordul).
Mindenesetre itt egy Makefile-részlet az érdekesség kedvéért:
%_clang.c: %.c
    (echo '#line 1 "$<"';\
     iconv -f ISO-8859-2 -t UTF-8 "$<") \
    >"$@"
K: A Borland-féle BCC32 fordító esetén mi a -finput-charset megfelelője?
V: A -CP<kódlapszám> opció, pl.:
bcc32 -v -CP1250 mysource.c
— Automatikus felismerés —
K: Van egy program, ami automatikusan felismeri a kódolást...
V: Nincs.
K: De, mondom, megvizsgálja a fájlt, és kiírja, hogy 'ANSI'...
V: Felejtsd el, nincs ilyen hogy 'ANSI', és nincs olyan, hogy automatikus felismerés.
K: Dehát még a Windows API-ban is benne van egy ilyen függvény: IsTextUnicode
V: És mégsem. Az ilyen eszközök kb. ilyesmire képesek:
K: Na jó, de akkor pontosan mi az az ANSI?
V: Ha egy program mondja neked, akkor azt jeleni, hogy 'valószínűleg nem UTF8'; ha te mondod egy Windows-os programnak, akkor a default egybájtos kódolást jelenti, ami lehet pl. az EASTEUROPE_CHARSET néven is ismert windows-1250, de lehet a nyugat-európában szokásos windows-1252. Vagy más.
K: Akkor mit tegyek?!
V: Mivel te ember vagy, a kódolás meghatározásához használd a természetes intelligenciádat, a hexviewert, meg ennek a fájlnak a végén a táblázatot. (Ha a fáljt egy ember állította elő, őt is megkérdezheted, de jó eséllyel nem fogja tudni; sőt, még csak be sem ismeri, hogy nem tudja, hanem kimondja az első/egyetlen kódolás nevét, ami az eszébe jut.)
— Iconv —
K: Sokat emlegeted ezt az iconv-ot, de igazából mi az?
V: Egy program (iconv executable) és egy programkönyvtár (libiconv) együtt; ha a te gépeden gyárilag nincs ilyen, akkor a GNU libiconv telepítését javaslom.
K: Ez a libiconv állítólag mindenre jó, nekem mégsem működik pl. a CP852-vel.
V: Lehet, hogy a te gépeden egy hiányos változat van, így ellenőrizheted:
$ iconv -l | grep 852
852 CP852 IBM852 CSPCP852
K: Jó, és ha tényleg nincs találat?
V: Fordít(tat)sd újra, de a configure-nél add meg a --enable-extra-encodings opciót!
K: Én?!
V: Vagy te, vagy a rendszergazda, aki azért kap fizetést, hogy a gépet üzemeltesse.
K: És ha pl. Perl CGI-t használok, abban is van iconv?
V: Akad: Text-Iconv.
K: Lehet saját kódtáblákkal bővíteni a GNU libiconv-ot?
V: Lehet, de a részletek leírása helyett csak két példafájlt adok, kiindulásnak jó lehet: IBM-037.TXT IBM-1047.TXT
— JavaScript —
K: JavaScript-ben hogyan lehet kódokat használni a stringekben?
V: \-szekvenciákkal, nevezetesen \xDD és \uDDDD; az előbbi két hexa számjegyet fogad, az utóbbi négyet, mindkét esetben unikódban. Pl:
<BUTTON onclick='alert ("\xe1rv\xedzt\u0171r\u0151 t\xfck\xf6rf\xfar\xf3g\xe9p");'>Próba</BUTTON>
Figyeljük meg, hogy az ő-t és az ű-t csak a hosszabb formával lehet megadni!
K: És ha U+ffff-nél nagyobb unikódot akarok használni?
V: Helyettesítő párt (surrogate pair) használva, pl. az U+1f354 kódú hamburger(🍔) esetén:
<button onclick='alert ("\\ud83c\\udf54 = \ud83c\udf54");'>Próba</button>
Egy másik lehetőség (ami újabb fejlesztés, tehát nem biztos, hogy mindenhol használható) ez szintaxis: \u{1-8 hexa számjegy}, pl:

K: JavaScriptről szólva, ha a window.open-nel nyitok meg egy ablakot, de úgy, hogy az első paraméter üres string, és az ablak tartalmát document.write-okkal állítom elő, az milyen kódolású lesz?
V: Tapasztalataim szerint UTF-8, pl:

K: Találtam egy encodeURIComponent nevű függvényt, amit tudnék használni a GET/POST paraméterek kódolásához (pl. Ajax alkalmazásánál), de nem tudom beopciózni, hogy milyen kódolás szerint képezze a hexakódokat, fixen UTF-8-at használ, pl: 'répa' -> 'r%C3%A9pa'.
V: Én sem tudom. A legjobb megoldás, ha a fogadó oldalon (PHP, CGI) is UTF-8-at használsz, vagy ha nem, akkor konvertálsz róla (iconv) a használt kódra.
K: Konvertálhatok, igen, de akkor nem lehet ugyanazt a php programot használni hagyományos meghívással és pl. Ajax-szal.
V: Megpróbálhatod a paraméterek nevével jelezni a kódolást is, pl egy _U a név végén UTF-8-at jelezhet, a feldolgozó program ebből tudhatja, hogy kell-e konvertálni, egyben az esetleges későbbi átállást is fokozatossá lehet így tenni). Pl:
valami.php?zoldseg=r%E9pa      -- 'hagyományos hívás', latin2
valami.php?zoldseg_U=r%C3%A9pa -- 'encodeURI-s hívás', utf-8
— PHP —

K: Az jó, ha a header-t nem a lap legelejére teszem, hanem az ob_* függvényekkel kavarok?
V: Szerintem hülyeség, de ha neked jó...

K: Pontosan hogyan kell használni az utf8_encode függvényt?
V: Sehogy, mert csak latin1-re működik. Válassz ezekből: iconv, mb_convert_encoding,
K: Akkor talán az utf8_decode függvényt sem érdemes használni?
V: Bingó! Az utf8->latin1 konverzió során az őŐűŰ betűk megszűnnek létezni.
K: Vannak a PHP-nek utf8-as kódolású stringek kezelésére szolgáló függvényei?
V: Akadnak: PHP: Multibyte String - Manual Pl.:
$elso_szo= mb_substr ("Árvíztűrő tükörfúrógép", 0, 9, "UTF-8");
Megj: Nem kell mindig kiírni a kódkészletet (vagyis az UTF-8-at), ha használod az mb_internal_encoding függvényt.

K: Milyen bajt okozhat, ha pl. a hagyományos substr függvényt használom?
V: Mondjuk az, hogy ügyesen kettévágsz egy utf8-szekvenciát...

K: Mit tegyek, ha az fgetcsv függvény hibásan működik, ha ékezetes betűk vannak a mezők elején?
V: Előzőleg egy setlocale (LC_CTYPE, "...") hívás segíthet. (Szerk. megj.: A problémát nem tudtam reprodukálni, a PHP verziójától is függhet.)

K: Igaz, hogy a htmlentites függvény is okozhat gondot?
V: Okozhat, mivel ez a függvény mindent &-szekvenciává alakít, amit egyáltalán át lehet alakítani; ez magában foglalja a magyar ékezetes betűket is, viszot a latin2-t nem támogatja (még ha meg is adjuk a harmadik paraméterben), vagyis az Ű-ből például &Ucirc; lesz (eredménye: Û). UTF-8 esetén az őŐűŰ betúket nem alakítja át. (Ez persze nem akkora baj, hiszen a többit sem kellene átalakítania.)

K: Akkor mit használjak helyette?
V: A htmlspecialchars-t, ami csak az alábbiakat konvertálja: & < > ' " (ez utóbbi kettő opcionális, lásd a leírásban: ENT_NOQUOTES=egyiket sem, default=csak a macskakörmöt, ENT_QUOTES=mindkettőt)

K: A htmlspecialchars-ról szólva, mi a harmadik paraméter, az encoding jelentősége?
V: Az, hogy fixen 'ISO-8859-1'-et kell odaírni, hogy jól működjön, függetlenül attól, hogy milyen kódolást használsz. Ez is volt az alapértelmezés az 5.4-es verzió előtt, de akkor megváltoztatták 'UTF-8'-ra, ami azt ereményezi, hogy az UTF-8-ként nem értelmezhető input-stringre üres stringet ad válaszul. (Hibaüzenet nélkül persze.)
Megj.: Kérlek, ne értelmezd ezt úgy, hogy szerintem az ISO-8859-1 kódolás jó lenne a magyar betűkhöz, mert nem így van. Csakis és kizárólag a PHP hibája, hogy ezen függvény ezen paraméterében nem lehet sem 'ISO-8859-2'-t, sem 'BÁRMI'-t megadni (hanem az 'ISO-8859-1' jelenti azt, hogy 'BÁRMI').
Kieg.: az 5.6-os verzióban ismét változott az alapértelmezés, ini_get('default_charset') az új érték. (Ez persze semmit sem változtat az előbbiek érvényességén.)
K: A már átalakított &-szekvenciákat hogyan tudnám visszacsinálni?
V: A html_entity_decode fügvénnyel. Akkor könnyebb használni, ha UTF-8-ban dolgozol, latin2 esetén az iconv-ra is szükség van:
function decode_utf8 ($s)
{
    return html_entity_decode ($s, ENT_QUOTES, 'UTF-8');
}

function decode_latin2 ($s)
{
    $u= html_entity_decode ($s, ENT_QUOTES, 'UTF-8');
    return iconv ('UTF-8', 'ISO-8859-2', $u);
}
K: Azt már értem, hogy az adatbázisba nem tudok akármilyen stringet beletenni, hanem némi escape-elésre van szükség (legalábbis ha nem lehet bind-változókat használni) pl.: mysql(i)_real_escape_string, de úgy látom, hogy (legalábbis bizonyos szervereken), valaki más is csinál valami hasonlót, ezzel szépen elrontva az adataimat!
V: Ez a magic_quotes_gpc nevű beállítás a php.ini-ben; ha nem vagy rendszergazda a gépen, akkor nem tudod kikapcsolni, hanem a programból kell a stripslashes függvényt használni, pl:
$pwd = $_REQUEST ['jelszo'];
if (get_magic_quotes_gpc ()) {
  $pwd = stripslashes ($pwd);
}
$mysql_pwd = mysql(i)_real_escape_string ($pwd, $mysql_conn);

Kieg: Jó hír, hogy az 5.4-es verzióban a magic_quotes megszűnt.

K: Hol is van ez a php.ini nevű configfájl?
V: Platformfüggő, de a phpinfo nevű fügvény elárulja:
$ echo '<?php phpinfo(); ?>' | php 2>/dev/null | \
    grep "Loaded Configuration File"
Loaded Configuration File => /usr/local/lib/php.ini

K: Első problémám: hol van itt a webserver meg a böngésző?
V: Sehol, ez egy standalone PHP volt, ha még nem ismered, akkor sürgősen barátkozz meg vele.
K: De ez nem megy Windows-ban!
V: Kicsit lejjebb olvashatsz a PHP használatáról Windows alatt.
K: Második problémám: tényleg kiírt egy fájlnevet, de olyan file nincs is a gépemen!
V: Akkor jó esélyed van rá, hogy ha lenne ilyen fájlod, akkor azt használná.
K: Hogyan csináljak?! Én csak programozó vagyok, nem fájlteremtő feketemágus!
V: Valószínűleg már van is ott két minta, php.ini-production és php.ini-development néven. Ha nem tudod, hogy melyikből indulj ki, akkor válaszd a development-et.
K: Windows-ban is használható a PHP? És a stand-alone változat is?
V: Mindkettőre igen a válasz; a stand-alone futtatható program a PHP.EXE; ha kényelmesen akarod használni, jól teszed, ha a PHP könyvtárát (tipikusan C:\PHP) elhelyezed a PATH-on.
K: Windows-ban is használhatók a PHP-bővítmények, mint például a FPDF?
V: Igen; telepítheted őket, ahová jónak gondolod, csak ne felejtsd el a php.ini-ben az include_path-t beállítani!
Példa:
C:\> mkdir \Php-Ext\Fpdf
C:\> cd \Php-Ext\Fpdf
C:\Php-Ext\Fpdf> unzip C:\download\FPDF17.ZIP
C:\Php-Ext\Fpdf> find "include_path" c:\php\php.ini
include_path=".;C:\PHP-EXT"
Megjegyzés: egy kis Windows-os minta batch-fájlt láthatsz itt is.
Egyéb Windows-os kérdések a FAQ-ban itt találhatók.
K: Ha egy pillanatra visszatérhetnénk a UNIX-hoz, igaz, hogy a php.ini helye nincs egészen kőbe vésve, különösen akkor nem, ha forrásból telepítem a PHP-t?
V: Ez igaz, ha nem vágysz kellemetlen meglepetésekre, akkor szimlinkekkel termethetsz rendet, pl:
ls -l /usr/local/etc/php.ini /usr/local/bin/php.ini /usr/local/lib/php.ini
-rw-r--r-- 1 root system 67184 Jan 12 16:40 /usr/local/etc/php.ini
lrwxrwxrwx 1 root system    14 Jun 19  2013 /usr/local/bin/php.ini -> ../etc/php.ini
lrwxrwxrwx 1 root system    14 Jun 19  2013 /usr/local/lib/php.ini -> ../etc/php.ini
Az első a valódi fájl, a másik kettő szimlink.
K: Azt értem, hogy PHP-ben használhatok \xXX szekvenciákat. Használhatok \uXXXX szekvenciákat is, mint C-ben vagy Java-ban?
V: Nem, de van kerülőút: használhatod erre a célra a json_decode függvényt, ennek az eredménye mindig UTF-8 lesz; pl:
    $utf8str= json_decode ('"\u0150 \u0151 \u0170 \u0171 \ud83c\udf54"');
    printf ("s=%s  hex=%s\n", $utf8str, bin2hex ($utf8str));

    s=Ő ő Ű ű 🍔  hex=c590 c591 c5b0 c5b1 f09f8d94
— Java —
K: Hogy tudok Javában String és byte-vector között konvertálni?
V: A String egyik konstruktora illetve a getBytes metódus szolgál erre.
K: Ha jól látom, vannak olyan változatok is, amelyeknek nem kell charsetName paraméter. Azok mire jók?
V: Hogy kibabrálhassál magaddal, illetve hogy platformfüggő legyen a működés.
K: Mindesetre, ha oda-vissza elvégzem a konverziót, akkor az eredetit kapom vissza, ugye?
V: Kivéve, ha nem. Pl. egybájtos kódolás esetén az "ôõő"-ből vagy "ôõ?" lesz, vagy "ô?ő". Vagy akár "???", szerencse kérdése.
K: És az igaz, hogy a 16-bites char típusban minden unikód elfér?
V: Hát tulajdonképpen nem, de az int típusban igen, illetve char-tömbben, vagy String-ben, UTF16 kódolással.
K: Vagyis szükség esetén 'helyettesítő párok' (magyarul 'surrogate pairs') használatával?
V: Igen, például a kedvenc hamburgerünket így tárolhatjuk String-ként: "\ud83c\udf54"
K: Ha Javában textfájlokat akarok írni/olvasni, a fájl-tartalom milyen kódolás szerint fog előállni/értelmeződni?
V: Valamilyen alapértelmezés / globális beállítás fog érvényesülni (lásd ezt a pontot is). Vagy használhatsz olyan metódusokat (pontosabban konstruktorokat), amiknek paramétere a karakterkészlet neve, pl:
  InputStreamReader (InputStream in, String charsetName)
  OutputStreamWriter (OutputStream out, String charsetName)
  PrintStream (File file, String csn)
K: JNI esetén hogyan férhetek hozzá egy String tartalmához?
V: Három lehetőséget látok: K: Ebből a 'módosított' részt nem értem pontosan...
V: A 'surrogate pair' elemeit külön-külön alakítja hárombájtos szekvenciáva, továbbá a nullás kódú karakterből C080 szekvenciát csinál, például "&#x1f354;&#0;" ilyen lesz módosított UTF-8-ban: ED-A0-BC—ED-BD-94—C0-80 (Közönséges UTF-8-ban ilyen: F0-9F-8D-94—00). Bővebben lásd itt: wiki: Modified UTF-8
K: A javac fordítóprogramnak kell tudnia, hogy a forrásprogram milyen kódolásban készült? És hogyan mondhatom meg neki?
V: Igen, a -encoding paraméterrel; ha nem adod meg, akkor valamilyen alapértelmezés jut érvényre, ami a konkrét esetben vagy jó, vagy nem. Az UTF-8 általában jó választás Java forrásprogramokhoz, de BOM ne legyen a fájl elején!
K: Szóval a Java forrásprogramok string-konstansaiban használhatok \uXXXX szekvenciákat. És string-konstansokon kívül is használhatom őket?
V: Igen, bárhol a forrásprogramban; ezeknek az értelmezése minden más fordítási lépés előtt történik, ami váratlan következményekhez is vezethet; íme egy egyszerű példa:
public class hellocomment {
    public static void main (String args[]) {
        // Where is the code? \u000d System.out.println ("Hello, comment\u0022);
    }
}
Itt a \u000d sorvégejelnek számít, tehát ami mögötte van, az nem megjegyzés; a \u0022 pedig a "macskaköröm", ami lezárja a stringet.
K: IBM MQS-t használnék Javából. Igaz, hogy az egyes üzenetek karakterkódolását külön-külön lehet beállítani/lekérdezni?
V: Igen, erre való a com.ibm.mq.MQMessage.characterSet mező. Persze egyedi IBM-es kódokat kell használni, pl:
	  37	ebcdic/ibm/latin1-compatible
	 850	cp850
	 852	cp852
	 819	iso-8859-1
	 912	iso-8859-2
	1208	utf-8
K: Milyen műveleteket használhatok bináris üzenetekhez?
V: Itt van rögtön a write, a getDataLength és a readFully. (Ezek byte-tömbökkel dolgoznak, tehát semmiféle konverzió nem történik.)
K: Milyen műveleteket használhatok karakteres üzenetekhez?
V: Íráshoz van egy writeString művelet; olvasáshoz a readStringOfByteLength metódust ajánlom, a getDataLength metódussal kombinálva (ezek gondoskodnak a karakterkonverzióról (lásd fentebb a characterSet mezőt).
Ha esetleg olyan ősrégi kliensed van, amelyben még csak sima readString művelet van, akkor a multibyte-os kódolásokhoz valamilyen helyettesítő megoldást kell tákolni, pl.:
        message.seek(0);    /* álljon az üzenet elejére */
        int blen= message.getDataLength ();
        String s;

        if (message.characterSet==1208) { /* csak egy multibyte-os kódolást kezelünk, az UTF8-at */
            byte [] binmsg = new byte [blen];
            message.readFully (binmsg);

            s = new String (binmsg, "UTF-8");
        } else {
            s = message.readString (blen);
        }
        return s;
— SQL-injection —
K: Mi is az Sql-injection lényege?
V: Legvilágosabban ezen az ábrán mutatják be: http://xkcd.com/327/
K: Nem értem, miért okozna gondot ez a név?
V: Akkor nézzük meg, hogyan működött a program:
Ezt akarta a programozó:
INSERT INTO students (name) VALUES ('<insertnamehere>');
De ez lett belőle:
INSERT INTO students (name) VALUES ('Robert'); DROP TABLE students; -- ');
K: Azannya! De biztos az, hogy ilyen támadás minden adatbáziskezelőben és minden kontextusban lehetséges?
V: Természetesen nem: előfordulhat, hogy a te esetedben csupán hibaüzenetet, hibás működést lehet így előidézni, nem pedig teljes adattörlést. Akarsz kockáztatni?
K: Jó, persze, nem, de azért azt megkérdezem, hogy miért csak SQL-injection van, miért nincs például HTML-injection, PHP-injection, shell-script-injection, és a többi?
V: Megnyugtatlak: ezek mind előfordulhatnak, és mind ellen védekezni kell. Alapvetően két gond van:
1. A hibás (vagy rossz-szándékú) adat olyan karaktereket tartalmaz, amiknek nem szabadna ott lenniük.
2. Az adat teljesen legálisan tartalmaz olyan karaktereket, amik megzavarhatják pl. az SQL-értelmezőt.
K: Akkor már értem, mi a baj. És mi lenne a megoldás?
V: Nos, ha például előre tudjuk, hogy mik a megengedhető karakterek, akkor ellenőriznünk kell az input-ot. Egy példa PHP-hez:
/* csak magyar kis- és nagybetűk, valamint számjegyek (és nem üres!) */
function Test ($s)
{
    $pattern= '/^[0-9a-záäéíóöőúüűA-ZÁÄÉÍÓÖÚÜŰ0-9]+$/';
    printf ("check(%s)=%d\n", $s, preg_match ($pattern, $s));
}
Egy pedig Javához:
/* csak magyar kis- és nagybetűk, valamint számjegyek (és nem üres!) */
static void Test1 (String s) {
    boolean fOk= s.matches ("^[0-9a-záäéíóöőúüűA-ZÁÄÉÍÓÖÚÜŰ0-9]+$");

    System.out.println ("Check ('" + s + "'): " + fOk);
}
K: És ha az input legálisan is tartalmazhat mindenféle jeleket?
V: Az egyik legjobb lehetőség az adatok és a kódok elválasztása; SQL-esetében a bind változók valók erre (lásd a következő fejezetben). Ha erre nincs lehetőség, a helyzettől függő módszereket kell használni ahhoz, hogy 'veszélyes' karaktereket tartalmazó adatot is jól kezeljünk.

1. példa: Oracle esetében a string-literál belsejében lévő aposztrófákat meg kell duplázni:
select 'Ezt mondta: ''hello''' AS idezet from dual;
IDEZET
-------------------
Ezt mondta: 'hello'
Ha pl. Javában alkotunk, ilyesmit írhatunk:
String ora_sql = "UPDATE tabla SET mezo='"+param.replaceAll("'", "''")+"'";
PHP-s példa:
$ora_sql = sprintf ("UPDATE tabla SET mezo='%s' WHERE ...",
    str_replace ("'", "''", $param));

2. példa: MySql esetében (általában) a \backslash használható escape-characterként a string-literálokban (PHP esetén lásd az addslashes illetve mysql(i)_real_escape_string függvényeket):
select 'Ezt mondta: \'hello\'' AS idezet;
Megj: Egyes egzotikus két- vagy több-bájtos kódolások esetén (az UTF8 nem ilyen) a \backslash része lehet egy bájt-szekvenciának, tehát ilyenkor nem jó az addslashes. Mivel ezek a szekvenciák a magyar nyelvhez nem használatosak, nem kell különösebben aggódnunk; mindenesetre arra ügyeljünk, hogy a(z esetleg rossz-szándékú) felhasználó ne avatkozhasson bele a SET NAMES-be.

3. példa: Ha HTML-be akarunk ilyen stringet illeszteni, használjuk az &-szekvenciákat – PHP esetén erre való a htmlspecialchars függvény (lásd ezt a pontot is!):
    $s= '<B>veszélyes szöveg <script>benne gonosz script</script></i>';
    print htmlspecialchars ($s, ENT_QUOTES, 'ISO-8859-1');
Java esetén különféle külső eszközök használhatók, de ha épp egyik sincs a kezünk ügyében, akár kézi erővel is alkothatunk valamit:
static String htmlescape (String s) {
    String sout=
        s.replaceAll ("&", "&amp;")
         .replaceAll ("<", "&lt;")
         .replaceAll (">", "&gt;")
         .replaceAll ("\"","&quot;")
         .replaceAll ("\'","&#x27;");
    return sout;
}
Megj: Például ezt a programrészt is át kellett alakítani ahhoz, hogy a HTML-ben jól jelenjen meg.
Ugyanez JavaScript-ben ilyesmi lenne:
function htmlescape (s) {
    var sret=
       s.replace (/&/g, '&amp;')
	.replace (/</g, '&lt;')
	.replace (/>/g, '&gt;')
	.replace (/\"/g,'&quot;')
	.replace (/\'/g,'&#x27;');
    return sret;
}

4. példa: PHP-ből akarunk shell-scriptet hívni. (Nagyon problémás terület, szívesebben ajánlanám az environment változók használatát!):
    $gyanus= "\"; rm -rf /fontos/dir/*; echo \" $(rm /important/)";
    $cmd= sprintf ('echo "Naplósor: %s" >>Naplo',
	addcslashes ($gyanus, '"$`\\'));
    system ($cmd);

5. példa: PHP és JavaScript között akarunk adatot cserélni. Erre való a JSON tehcnológia (lásd még: wiki:JSON, PHP:JSON, w3schools:JSON) ami komplett tömböket és struktúrákat is szépen kezel. Viszont, ha méréseim nem csalnak, csak UTF-8-at szeret. (Újabb érv az átállás mellett!).

6. példa: CSV fájlt akarunk előállítani/elemezni. Ugyebár ennél semmi sem egyszerűbb, de azért gondoljunk a következőkre:
(1) ha egy mezőben elválasztójel (magyar beállítás esetén vesszőspont), sorvége, vagy idézőjel (rendszerint "macskaköröm") van, akkor a mezőt kötelező idézőjelek közé tenni, egyébként csak szabad idézőjelek közé tenni.
(2) ha egy mező idézőjelek között van, akkor a benne előforduló idézőjeleket meg kell duplázni.

7. példa: shell-ben írt CGI-program fájlneveken megy végig, és <img src="fájlnév"> tagokat állít elő. Nincs semmi gond, amíg egy haxor létre nem hoz egy abc"><h1>Rossz az oldalad.jpg nevű fájlt (ami teljesen legális fájlnév Unixban!), és ezzel elcsúfítja a kimenetet. Megoldási lehetőség a shell scriptben:
for i in *.jpg; do 
    i2="$(printf '%s' "$i" | sed 's/\"/\&quot;/g')"
    printf '<img src="%s">\n' "$i2"
done
K: Erről jut eszembe: esetleg ilyesmi okból szüntették meg PHP-ben a register_globals intézményét?
V: Bizony ám! A register_globals okozta gondot nevezhetjük akár PHP-injection-nak is.
K: Ugye az UTF-8-nak nincs semmi köze ehhez, vagy más biztonsági kérdésekhez?
V: Semmi, kivéve azt apróságot, hogy nem szabad elfogadni olyan UTF-8 kódolású inputot, amelyben előfordul olyan karakter, ami nem a lehető legrövidebb módon van kódolva. Például a 27 hexkódú 'aposztrófát' lehetne hosszabban is kódolni (C0A7, E0C0A7 vagy F0C0C0A7), és ezzel esetleg megkerülni az ellenőrző programrészeket, de a szabvány szerint az ilyen szekvenciákat érvénytelennek kell tekinteni.
— Bind-változók —
K: Mi is az a bind-változó?
V: Részletesen lásd itt: wiki:Prepared statement, a lényeg az, hogy az adatok nincsenek az SQL-utasításba beleágyazva, hanem külön mennek át. Példa:
volt: INSERT INTO t_login VALUES ('userid', 'jelszó')
lett: INSERT INTO t_login VALUES (?, ?)
K: De így külön hívások kellenek az adatok megadásához, vagyis a program bonyolultabb és lassabb lesz!
V: Valóban bonyolultabb, mivel a végrehajtás nem egy, hanem három részből áll: elemzés (parse vagy prepare), értékek megadása (binding) és végrehajtás (execute); viszont adott esetben lehet, hogy a művelet nem lassabb lesz, hanem gyorsabb.
K: Vajon mitől?!
V: Attól, hogy így nagy az esélye annak, hogy a szerverhez többször egymás után ugyanaz az SQL-utasítás érkezik (az utasításba beépített értékek esetén ez nem valószínű). Ennek az az előnye, hogy a szerver (amely gyorstárazza a legutóbbi utasításokat), felismeri az utasítást, és így a munka egyik időigényes részét, az elemzést megspórolja.
K: PHP és MySql esetén használhatók bind változók?
V: A mysqli modul használatával. A PHP-ben használható egyéb adatbáziskezelőről itt olvashatsz: PHP: Database Extensions
K: Ilyen bind-változók használhatók más kontextusban is? Például PHP-ből shell-hívásakor?
V: A környezeti változók (environment variables) használhatók erre a célra, pl:
<?php
/* felhasználótól jövő megbízhatatlan input */
    $s = '"; echo én vagyok a haxor és törlök; rm legfontosabb_file #';

/* veszélyes megoldás */
    $cmd= sprintf ('echo "Test#1 %s" >>kedvesnaplom', $s);
    system ($cmd);

/* biztonságos megoldás */
    putenv ('param1='.$s);
    $cmd= 'printf -- \'Test#2 %s\\n\' "$param1" >>kedvesnaplom';
    system ($cmd);
?>
Az utóbbi helyzetben a param1-ben lévő idézőjelek, joker-karakterek, \-szekvenciák stb. nem jutnak érvényre.
Ugyanez Perl-ben:
$NAME="John Doe'; pwd -P; echo 'I escaped your quotes";

system ("echo Username='$NAME'"); # Haxor wins

$ENV{'param_1'}=$NAME;
system ("echo Username=\"\$param_1\""); # Haxor loses
K: Ha már így feljött a shell-hívása, van-e még valamilyen fontos szabály, amit tudnom kell?
V: Igen: hogy ha csak lehet, kerüld el, mert nagyon lassú. Nagyon sok mindenre van PHP-beli megoldás, ne légy rest, használd a php.net keresőjét.
K: Shell-hívásról szólva, pl. C-nyelv esetén is használhatunk így environment változókat?
V: Igen, pl. a system és popen függvények esetében; a változók beállítására és törlésére használhatjuk a setenv és unsetenv függvényeket:
setenv ("param1", dangerous_value, 1);
system ("printf -- '%s\\n' \"$param1\" >>naplo");
unsetenv ("param1");
Kevésbé korszerű esetben mindkettő a putenv függvénnyel történhet:
char env_tmp[64];

sprintf (env_tmp, "param1=%.*s", (int)(sizeof env_tmp -8), gyanus_input);
putenv (env_tmp);
system ("printf -- '%s\\n' \"$param1\" >>naplo");
putenv ("param1=");

— Miegymás —

K: Ha a böngésző az ékezetes karakter helyett egy olyan szekvenciát küld, hogy &#unikód; az mi a manót jelent?
V: Azt, hogy a felhasználó olyan karaktert írt (pasztázott) az input-mezőbe, ami nincs benne az oldalad által használt kódban (illetve a <FORM> tagban megadott accept-charsetben). Például, ha latin2-t állítasz be, akkor a hullámos Õ betű helyett ilyen szekvenciát (&#213;) küld a Firefox.

K: Szóval ez még böngészőfüggő is? Mi lehet a megoldás?
V: Mondjuk az UTF-8 használata, amit persze kiegészíthetsz azzal, hogy szűröd a beérkezett inputot: pl. amit az iconv nem tud konvertálni a neked tetsző kódkészletre (windows-1250, mondjuk), azt elutasítod.

— WinDos —
K: A DOS-ablakban nem jól jelennek meg az ékezetes betűim...
V: A DOS-ablakról azt kell tudnod, hogy ott a CP852 nevű kódolás érvényesül, ha a text-editorod nem tudna bele/belőle konvertálni, az iconvra mindig számíthatsz:
iconv -f latin2 -t cp852 <winfile >dosfile
iconv -f cp852 -t utf-8 <dosfile >utf8file
K: Nem lehet átállítani másra, pl win-1250-re?
V: De lehet, két dolog kell hozzá: az egyik, hogy a karakterkészlet ne a Raster Fonts legyen, hanem a Lucida Console (Tulajdonságok/Font/Font), a másik a CHCP 1250 parancs futtatása. A CMD.EXE esetében a registry-ben megadható, hogy ezt automatikusan futtassa: HKLM\SOFTWARE\Microsoft\Command Processor\AutoRun-ba: CHCP 1250 >NUL Egy másik lehetőség, hogy a saját programodból hívod meg: system ("CHCP 1250");
K: És ha nem globálisan akarom beállítani, hanem úgy, hogy csak a saját programomra vonatkozzon?
V: Ha a programod egy Windows-alkalmazás, akkor az alábbi kódrészlet segít:
SetConsoleCP (1250);
SetConsoleOutputCP (1250);
Egyéb esetben ezzel próbálkozhatsz:
system ("CHCP 1250");
K: Egyéb lehetőségek?
V: Például megteheted, hogy eleve DOS-os szövegszerkesztőben írod a programodat, amilyen pl. a NCEDIT. További lehetőség a konverzió, például a Windows beépített függvényeivel: CharToOem CharToOemBuff OemToChar OemToCharBuff. Vegyük észre, hogy ezeknél sem a forrás sem a cél karakterkészletet nem kell megadni, azt a Windows magától állapítja meg, a Területi beállítások (és talán a billentyűzetkiosztás(?)) alapján. Lásd még: GetACP GetOEMCP Code Page Identifiers.
K: A setlocale függvényt hogyan használjam, hogy ezzel összhangban legyen (és jól működjenek az isalpha- és toupper-szerű makrók)?
Itt a LC_CTYPE beállítása számít, több lehetőség van, pl.:
    setlocale (LC_CTYPE, ".1250");	/* fixen windows-1250 */
    setlocale (LC_CTYPE, ".852");	/* fixen IBM-852 */
    setlocale (LC_CTYPE, "");		/* a gép default kódolása, valószínűleg azonos a következővel: */
    setlocale (LC_CTYPE, ".ACP");	/* a gép default "ANSI" kódolása */
    setlocale (LC_CTYPE, ".OCP");	/* a gép default "OEM" kódolása */
    setlocale (LC_CTYPE, "hu-HU");	/* alapértelmezett magyar kódolás, valószínűleg windows-1250 */
    setlocale (LC_CTYPE, "hun");	/* alapértelmezett magyar kódolás, valószínűleg windows-1250 */
Megj: Ezt persze a fenti CHCP-vel (és a programíráskor használt kódkészlettel) összhangban érdemes beállítani; ne felejtsük el, hogy a LC_ALL magában foglalja a LC_CTYPE-ot is.
K: UTF8-at is lehet használni a DOS-ablakban?
V: Igen, a beállítás: system ("CHCP 65001");
K: Ez így biztos jó lesz?
V: Nem biztos. Régi Windows-ok (pl. XP) ezt azzal nyugtázzák, hogy az illető CMD.EXE példány képtelenné válik batch-ek (.BAT, .CMD) futtatására. (Ha indítunk egy batch-et, hibaüzenet nem jön, de a batch sem hajtódik végre.) Például a TortoiseCVS CVS.EXE programja csinál ilyet.
K: A Command Prompt-ban futtatott java ékezetei sem egészen jók... vagy egy magyar betű sem jó, vagy csak az ő/ű nem jó.
V: Próbálkozz meg az alábbiak valamelyikével (az első kettő nagyjából ugyanaz, akkor használhatóak, ha volt CHCP 1250 előtte, a harmadik akkor, ha az alapértelmezett CHCP 852 van érvényben, az utolsó pedig az UTF8 (CHCP 65001) esetére):
java -Dfile.encoding=iso-8859-2 classname
java -Dfile.encoding=windows-1250 classname
java -Dfile.encoding=cp852 classname
java -Dfile.encoding=utf-8 classname
K: Jól látom, hogy Windows-ban szimlinket sem tudok csinálni?
V: NTFS fájlrendszeren tudsz könyvtárra mutató symlinket csinálni, a junction nevű programmal. Példa a használatára:
 JUNCTION C:\jdk "C:\Program Files\Java\jdk1.6.0_04"
 SET JAVA_HOME=C:\jdk
 PATH %JAVA_HOME%\bin;%PATH%
Ha ezután új verziót telepítünk, csak a szimlinket kell átirányítanunk rá, minden más maradhat változatlan.
Megj: Újabb Windows-okon van erre beépített parancs is, a mklink, az nem csak könyvtárra, hanem fájlra mutató szimlinket is tud csinálni.
K: Windows-ról szólva, a billentyűk kódjairól (VK_***) hol tudok tájékozódni?
V: Itt: msdn: Virtual-Key Codes
K: Pont a magyar billentyűzet magyar betűit nem látom ott...
V: Saját méréseim a következőt mutatják:
Bill Kód  Név
 É   BA   VK_OEM_1
 Ó   BB   VK_OEM_PLUS
 Ü   BF   VK_OEM_2
 Ö   C0   VK_OEM_3
 Ő   DB   VK_OEM_4
 Ű   DC   VK_OEM_5
 Ú   DD   VK_OEM_6
 Á   DE   VK_OEM_7
 Í   E2   VK_OEM_102
K: És a WM_CHAR üzenetnél milyen kódot kapok a wParam-ban? A Windows aktuális ANSI kódját (pl windows-1250), vagy unikódot?
V: A RegisterClass milyenségén múlik: ha az RegisterClassW volt, akkor unikód, egyébként ANSI.
Megj: Az ablaknak ezt a tulajdonságát le is lehet kérdezni az IsWindowUnicode függvénnyel.
K: Van Windows-ban egy olyan, hogy Vezérlőpult / Terület és nyelv / Felügyelet / Unicode szabványt nem támogató programok nyelve. Ez pontosan mit szabályoz?
V: Azt nem tudom, de valószínűleg nem lesz baj belőle, ha magyar (Magyarország)-ra állítod. Vigyázz, ez globális beállítás, tehát a gépen minden felhasználóra és minden programra vonatkozik.
K: Ennek van köze az alábbi registry-entrykhez?
HKLM\SYSTEM\CurrentControlSet\Control\Nls\CodePage\ACP
HKLM\SYSTEM\CurrentControlSet\Control\Nls\CodePage\OEMCP
V: Valószínűleg van.
K: A SetThreadLocale függvény pontosan mit szabályoz?
V: Hát, szinte biztos, hogy valamire valamilyen hatása van, kivéve, ha nincs. Ha úgy érzed, hogy használni akarod, akkor ebből a táblázatból válaszd ki a magyar beállításokat jelentő 1038 (0x040e) értéket. Ezt a számot így is előállíthatjuk:
    MAKELCID
	(MAKELANGID (LANG_HUNGARIAN, SUBLANG_HUNGARIAN_HUNGARY),
	 SORT_HUNGARIAN_DEFAULT)
— Linux —
K: Gondolom linuxban nincs ilyen probléma...
V: Ott is gondot okozhat, ha nem tudod, hogy a terminálon milyen karakterkódolás van érvényben (a leggyakoribb persze ott is a latin2 és az utf8). Az alábbiak valamelyike segíthet eligazodni:
locale | grep LC_CTYPE
echo ${LC_ALL-${LC_CTYPE-$LANG}}
echo -n 'ő' | od -tx1 # f5->latin2, c591->utf8
K: Csak a LC_CTYPE beállítása fontos az ékezetes betűk szempontjából?
V: Azért ez sem egészen biztos: előfordulhat, hogy ha a lokalizáció valamelyik aspektusa (például a LC_TIME) nem jó, akkor a setlocale(3) függvény is hibával tér vissza, amitől egyik-másik program úgy dönthet, hogy el sem indul, vagy pedig hétbites ASCII módban működik. (Ez utóbbit pl. egy Midnight Commanderrel idéztem elő.)
K: Említetted a LC_TIME-ot: ennél is számít, hogy hu_HU.ISO-8859-2 vagy hu_HU.UTF-8 van-e benne?
V: Bizony számít: amikor egy program (pl. a ls) a hónapok nevét akarja kiírni, pl a jún rövidítés latin2-ben három bájt (6a fa 6e), utf8-ban négy (6a c3 ba 6e), ezt a LC_TIME határozza meg.
K: A linux virtuális termináljain (Alt+F1...Fn) mi van, UTF-8, vagy 8-bites kódolás (pl ISO-8859-2), vagy szabályozható?
V: Szabályozható, programból az ioctl (KDSKBMODE, K_XLATE/K_UNICODE) rendszerhívással (az első a 8-bites);
shell-ből a kbd_mode -a/-u vagy az unicode_stop/unicode_start paranccsal;
rendszerinduláskor a vt.default_utf8=0/1 kernelparaméterekkel (lásd a lilo/grub append parancsát).
K: És, gondolom, a LC_CTYPE változót is ezzel összhangban kell beállítanom... Ugye azt teljesen szabadon állíthatom be?
V: Teljesen szabadon a localedef --list-archive által felsoroltak közül érdemes választanod; ha az nem elég, bővitsd a /etc/locale.gen listát, és futtasd a locale-gen-t (mindez disztribúciófüggő lehet, olvasd el a locale-gen manuálját).
K: De ha én mondjuk IBM852-es vagy Windows-1250-es kódolást szeretnék használni Linuxon, akkor megsemmisül a világegyetem, vagy ez is lehetséges?
V: Méréseim szerint ez is lehetséges, ha vannak ilyen fájlok a /usr/share/i18n/charmaps könyvtárban (ez persze disztribúciófüggő lehet), pl.:
$ cat /etc/locale.conf
...
hu_HU        ISO-8859-2
hu_HU.IBM852 IBM852
hu_HU.CP1250 CP1250
hu_HU.UTF-8  UTF-8
...
# locale-gen
Generating locales (this might take a while)...
...
  hu_HU.ISO-8859-2... done
  hu_HU.IBM852... done
  hu_HU.CP1250... done
  hu_HU.UTF-8... done
...
Generation complete.
$  LC_ALL=hu_HU.IBM852 mc # nézzük meg a HELP-et (F1)
K: És az emulált terminálokon (xterm és vidéke)?
V: Az emulátor az indulásakor a LC_CTYPE alapján dönti el, hogy egybájtos vagy unikódos módban működjön-e. Valahogy így lehet kipróbálni:
LC_CTYPE=hu_HU.ISO-8859-2 xterm -e sh -c 'showkey -a' &
LC_CTYPE=hu_HU.UTF-8      xterm -e sh -c 'showkey -a' &
A magyar ékezetes betűk billentyűit próbájuk ki, látni fogjuk a különbséget.
K: Mit csinál az emulátor, ha a szervertől kapott adat nem olyan kódolású, mint amit elvár?
V: Ha egybájtos üzemmódban van, és a szervertől UTF8-at kap, akkor nincs semmi gond, csak a felhasználó látja, hogy baj van, például tűzvész helyett tűzvész (latin1) vagy tĹązvĂŠsz (latin2) jelenik meg.
Ha UTF8-módban a szervertől egybájtos kódolású adat érkezik, akkor az emulátor döntésén múlik, hogy mit tesz az érvénytelen karakterekkel: eldobja őket, vagy helyettesíti kérdőjellel (vagy más 'hibajelző' karakterrel), vagy esetleg segíteni akar és latin1-ként értelmezve megjeleníti az inputot. Ez utóbbi esetben a latin2-es tűzvész helyett tûzvész jelenik meg, amit a felhasználó esetleg észre sem vesz, vagyis nem szerez tudomást arról, hogy a kliens és a szerver beállításai nincsenek összhangban.
K: Az emulátort futás közben is át lehet állítani egyik üzemmódból a másikba?
V: Egyes emulátorok (pl. xterm) esetén az alábbi escape-szekvenciákkal:
    ESC % G	UTF8-mód
    ESC % @	egybájtos kódolás
K: A LC_CTYPE-ról szólva úgy látom, a különféle Unixok nem egészen értenek egyet abban, hogy az ISO-8859-2 és az ISO8859-2 közül melyik a jobb.
V: Én sem tudom, hogy melyik a jobb, csak azt, hogy a felhasználóknak gondot okozhat ez a kettősség, pl. ha távoli eléréssel (ssh) érkezik egyikből a másikba. Ilyesmit lehetne valamilyen 'profile' fájlba tenni:
# ISO-8859 => ISO8859 irány,
# a változók listája értelemszerűen bővítendő
for var in LANG LC_CTYPE LC_ALL; do
    eval "OLDVAL=\$$var"
    TMPVAL=$(echo "$OLDVAL" | sed 's/ISO-8859/ISO8859/')
    if [ "$TMPVAL" != "$OLDVAL" ]; then eval "$var='$TMPVAL'"; fi
done
K: Még így is kapok egy warning-ot a bash-tól...
K: Akkor keményebb eszközhöz kell nyúlni, a neve szimlink. Pl. AIX esetén:
ln -s en_US.ISO8859-1 /usr/lib/nls/loc/en_US.ISO-8859-1
ln -s hu_HU.ISO8859-2 /usr/lib/nls/loc/hu_HU.ISO-8859-2
Megj: Ez nem az előző helyett ajánlom, csak annak kiegészítéseképpen.
K: Off-topik lenne, ha megkérdezném, hogyan állítsam be a TZ változót?
V: Hát, ha magyar időhöz akarod állítani, akkor nem annyira; a legfontosabb, hogy ha már működik a rendszered, akkor ne piszkálj hozzá, egyébként az alábbiak közül próbáld ki valamelyiket (az elsőhöz kell a timezone adatbázis (avagy Olson-adatbázis, zoneinfo-adatbázis) a gépedre, a másodikhoz nem)
export TZ=Europe/Budapest
export TZ=CET-1CEST-2,M3.5.0/2,M10.5.0/3
K: És ha egy negyven évvel ezelőtti dátumról kellene megállapítani, hogy volt-e akkor nyári időszámítás? (Mert mondjuk a fájlrendszer UTC-ben tárolja a fájlok keletkezési idejét, de a ls-nek LT-ben (azaz helyi idő szerint) kellene kiírnia.)
V: Az első megoldással ez lehetséges, a másodikkal nem.
K: Szóval nem lehet semmi baj, ha TZ=Europe/Budapest beállítást használok?
K: Elvileg semmi, de a gyakorlatban zavart okozhatnak például azok az esetek, amikor a nyári időszámítással járó óraelőreállítást 23:00-kor vagy éppen éjfelkor tartották; az utóbbi miatt például az '1941-04-08 00:00:00' időpont nem is létezett.
Néhány példa ilyen időpontokra (ezek 'téli időszámítás' szerint értendők):
    1916-04-30 23:00:00
    1941-04-08 00:00:00
    1945-05-01 23:00:00
    1954-05-23 00:00:00
    1955-05-23 00:00:00
    1956-06-03 00:00:00
Megj: a nemlétező időpontokat egyes programok visszautasítják, mások esetleg 'jószándékúlag korrigálják'; ez végeredményben egy óraval nagyobb időpontot jelenthet, ami különösen akkor látványos, ha az óraátállítás 23:00-kor történik, amikor korrekció miatt a dátum-rész is változik (a következő napra). Ez kétszer fordult elő: 1916.04.30-án és 1945.05.01-én.
K: Ha már így eltértünk a dátumok és idők felé, azt is megkérdezem, mit tegyek, ha dátumokkal szeretnék számolni (pl napokat hozzáadni/kivonni), de csak időpontok kezelésére való eszközöket használhatok? Ha megszorzom a napok számát 24*60*60-nal, hogy másodpercet kapjak, és azzal számolgatok, nem lehet bajom a nyári/téli időszámítás miatt?
V: Hogyne lehetne! A legjobb ötletem az, hogy a kérdéses dátumot egészítsd ki 00:00:00 időponttal, és úgy számolj vele, mint egy UTC-beli időponttal, pl egy bash-scriptben:
    Now="$(date +%Y%m%d)"
    Then="$(date --utc +%Y%m%d -d "$Now UTC -28203 days")"
    echo "$Now" "$Then"
    20180626 19410408
— Adatbázisok —

K: A PHP programom adatbázist is használ (pl. MySql-t), szükségszerű-e, hogy az adatbázis ugyanazt a kódolást használja, mint a szájt egésze?
V: Egyszerűsíti a dolgokat, de nem szükségszerű, php-ban is használhatod az iconvot (kivéve, ha szolgáltatód elfelejtette belefordítani, lásd a phpinfo-t).

K: Átállítottam az adatbázisom beállításait; az azóta bekerült adatok jók, de a régiek nem konvertálódtak át automatikusan!
V: Elhiszem.
K: És mit csináljak, hogy jó legyen?
V: Ha tesztrendszerről volt szó, akkor a DELETE FROM tábla; utasítás a barátod; ha fontos adatokról, akkor az export + kézi javítás + import eljárást ajánlom.

K: Ha nem tudom biztosan, hogy mi van az adatbázisban (például, hogy ahol kérdőjelet látok, ott tényleg kérdőjel van-e, vagy megjeleníthetetlen karakter), mit tegyek?
V: A tárolt érték hexa kódját kérdezd le, azt már tudod ellenőrizni. Bővebben lásd itt.
— MySql —
K: Hol lehetne olvasni a MySql idevágó beállításairól?
V: Például itt: MySQL 5.1 Reference Manual :: 9 Internationalization and Localization A MySql szerver képes konvertálni a programod által használt karakterkészletre/-ről, főleg, ha megmondod neki, hogy mi az. Példák:
SET NAMES 'utf8'
SET NAMES 'latin2'
Bővebben lásd itt: 9.1.4. Connection Character Sets and Collations
Kieg: PHP esetén itt van ugyanerre a célra a mysql(i)_set_charset is. Ezt a beállítást a lekérdezhetjük a mysql_client_encoding/mysqli_character_set_name függvénnyel.
Kieg2: Ez utóbbi az ajánlott módszer; mivel ekkor a MySql kliensoldali kódja is tudomást szerez a beállításról, nem csak a szerveroldal; ennek például a mysql(i)_real_escape_string függvény használatakor van jelentősége (egyes egzotikus kódolások esetén). Lásd még ezt a pontot.

K: Azt olvasom a php.net-en, hogy jó lenne elszakadni a mysql-től, és a mysqli felé mozdulni.
V: Szerintem kapkodni nem érdemes, de azért gondolkodj el a dolgon, ne várd meg, míg kihúzzák alólad a szőnyeget. Én személyesen legfőbb előnyének nem azt látom, hogy objektumokat lehet benne orientálni, hanem azt, hogy bind-változókat használhatunk vele.

K: A MySql világában az 'utf8' az ugyanaz, mint amit mindenki más UTF-8-nak nevez?
V: Nem egészen, náluk az 'utf8' azt jelenti, hogy a 0 és 65535 (0ffffH) közötti unikódok egyenként 1-3 bájton tárolva, míg a világ többi részén az UTF-8 azt jelenti, hogy a 0 és 2097151 (1fffffH) közötti kódok 1-4 bájton tárolva.

K: És hogy mondják MySql nyelven azt, amit mindenki más UTF-8-nak nevez?
V:: utf8mb4 a neve.

K: Pótkérdés: és az biztos, hogy az UTF-8-ban legfeljebb négybájtosak a kódok?
V: A korábbi szabvány (RFC2779) hatbájtos kódokat is megengedett, a későbbi (RFC3629) ezt négy bájtra csökkentette.

— Oracle —
K: Oracle-ről valamit?
V: Oracle-adatbázis esetén az Oracle kliensoldali programja végzi a koverziót a szerver és a felhasználó kódkészelete között, az előbbit értelemszerűen a szervertől tudja meg, az utóbbit a NLS_LANG környezeti változóból (Windows esetén esetleg registry-entryből), pl.:
export NLS_LANG=American_America.AL32UTF8
putenv ("NLS_LANG=Hungarian_Hungary.EE8ISO8859P2");
(Az első rész az üzenetek nyelvét, a második a területi beállításokat adja meg.)
Nem kell minden részt megadnunk, például csak a karakterkészlet beállítása:
export NLS_LANG=.EE8MSWIN1250
Megj.: Ezt a beállítást még az Oracle-connect előtt érdemes elvégezni, annál is inkább, mert (más beállításoktól eltérően) menet közben nem lehet módosítani ALTER SESSION paranccsal.
Kiegészítés: PHP esetén az oci_connect negyedik paraméterét is használhatjuk erre a célra.
Megj.: Ne felejtsük el, hogy ha 64-bites Windows-ban 32-bites Oracle-t használunk akkor a registry-ben nem a HKLM\SOFTWARE\ORACLE, hanem a HKLM\SOFTWARE\Wow6432Node\ORACLE a barátunk. (De még inkább a környezeti változó használata registry helyett.)
Érdekesség: A NLS_LANG-ot nem lehet AL16UTF16-ra állítani (illetve lehet, de nem fog működni). A magyarázat az, hogy ez a NLS_NCHAR szokásos/alapértelmezett értéke. Ez kb annyira logikus, mint hogy az ég színe nem lehet kék, hiszen a tenger színe a kék.
K: Jól tudom, hogy az Oracle-nek van egy saját CONVERT függvénye?
V: Igaz, használata: CONVERT (<mit>, <mibe>, <miből>)
Például, ha az Sql*Plus-t használod, és ISO-8859-2 a karakterkészleted (Oracle nyelven: EE8ISO8859P2), akkor így próbálhatod ki:
SQL> select dump (convert ('árvíztűrő', 'UTF8', 'EE8ISO8859P2'), 16) 
     as hexutf8 from dual;
HEXUTF8
----------------------------------------------------
Typ=1 Len=31: c3,a1,72,76,c3,ad,7a,74,c5,b1,72,c5,91
K: Jól látom, hogy az Oracle saját neveket használ a kódkészletekre?
V: Igen, továbbá egy egyedi számot is hozzárendel. Az alábbi konverziós függvények lehetnek érdekesek: NLS_CHARSET_ID, NLS_CHARSET_NAME, UTL_I18N. MAP_CHARSET. Példa:
select nls_charset_name(2000),
       nls_charset_id('ce8bs2000'),
       utl_i18n.map_charset('ee8iso8859p2',0,0)
from dual;
AL16UTF16  233  ISO-8859-2
Néhány fontos kódkészlet:
   1 us7ascii     us-ascii
  31 we8iso8859p1 iso-8859-1
  32 ee8iso8859p2 iso-8859-2
  10 we8pc850	  cp850
 150 ee8pc852	  cp852
 178 we8mswin1252 windows-1252
 170 ee8mswin1250 windows-1250
 870 al24utffss	  utf-8 (max. hárombájtos szekvenciák)
 871 utf8	  cesu-8
 872 utfe	  utf-8/ebcdic
 873 al32utf8	  utf-8
1000 utf16	  utf-16
2000 al16utf16	  utf-16be
2002 al16utf16le  utf-16le
K: Akkor most hányszor van az UTF8? (870-873 kódok)
V: A 870-es elavult (UniCode 1.1-nek megfelelő), maximum hárombájtos szekvenciák vannak benne; a 871-es igazából a CESU-8-at jelenti, ami az U+FFFF feletti kódokat 2*3 bájton ábrázolja. Új fejlesztéshez ne használd. (Megjegyzés: hasonló jelenség MySql-nél is van.) A 872-es UTFE EBCDIC-hez való; a 873-as AL32UTF8 a 'közönséges' UTF-8-nak felel meg.
K: És UTF16 is kétszer van?
V: Úgy tűnik. A kettő között vagy van különbség, vagy nincs. Említsük meg, hogy az oci.h fájlban van egy ilyen definíció:
#define OCI_UTF16ID 1000 /* UTF16 charset ID */
K: Erről jut eszembe: a NLS_LANG ez egyetlen hasznos változó lokalizáció ügyben?
V: Itt olvashatsz mindegyikról, pl. a dátum formátumának beállítása:
export NLS_DATE_FORMAT=YYYYMMDD.HH24MISS
Ezen beállítások nagy része menet közben is megváltoztatható, például magyar ABC szerinti rendezés beállítása SQL paranccsal így történhet:
alter session set nls_sort=hungarian;
A session beállításainak lekérdezése:
SQL> select * from nls_session_parameters;
PARAMETER               VALUE                       
----------------------- ----------------------------
NLS_LANGUAGE            AMERICAN
NLS_TERRITORY           AMERICA
NLS_SORT                BINARY                      
...
K: A szerver beállításait hogyan lehet lekérdezni?
V:A NLS_DATABASE_PARAMETERS a barátunk:
SQL> select *  from nls_database_parameters;
PARAMETER                      VALUE
------------------------------ ----------------------------------------
NLS_NCHAR_CHARACTERSET         AL16UTF16
NLS_LANGUAGE                   HUNGARIAN
NLS_TERRITORY                  AMERICA
NLS_CURRENCY                   $
NLS_ISO_CURRENCY               AMERICA
NLS_NUMERIC_CHARACTERS         .,
NLS_CHARACTERSET               EE8ISO8859P2
NLS_CALENDAR                   GREGORIAN
NLS_DATE_FORMAT                DD-MON-RR
NLS_DATE_LANGUAGE              HUNGARIAN
NLS_SORT                       HUNGARIAN
NLS_TIME_FORMAT                HH.MI.SSXFF AM
NLS_TIMESTAMP_FORMAT           DD-MON-RR HH.MI.SSXFF AM
NLS_TIME_TZ_FORMAT             HH.MI.SSXFF AM TZR
NLS_TIMESTAMP_TZ_FORMAT        DD-MON-RR HH.MI.SSXFF AM TZR
NLS_DUAL_CURRENCY              $
NLS_COMP                       BINARY
NLS_LENGTH_SEMANTICS           BYTE
NLS_NCHAR_CONV_EXCP            FALSE
NLS_RDBMS_VERSION              10.2.0.5.0

20 rows selected.
K: Oracle esetében a string-literálokban használhatok hexa-szekvenciákat?
V: Általában nem, de az unistr függvénnyel igen.
K: És mi ennek a függvénynek az inverze?
V: Még nem találtam ilyet, addig is össze lehet csapni valamit házilag:
function nchar_to_unistr (p_str in nvarchar2) return varchar2 is
    v_out   varchar2(32767);
    i       pls_integer;
    v_chr   nchar;
    v_code  pls_integer;
    v_hex   varchar2(8);
begin
    v_out:= '';
    for i in 1..length(p_str) loop
        if i = 1 then v_out := 'unistr('''; end if;
        v_chr  := substr (p_str, i, 1);
        if v_chr between ' ' and '~' and v_chr not in ('''', '"', '\') then
            v_out  := v_out || v_chr;
        else
            v_code := ascii (v_chr);
            v_hex  := to_char (v_code, '0xxxx');
            v_hex  := substr (v_hex, length (v_hex)-3, 4);
            v_out  := v_out || '\' || v_hex;
        end if;
    end loop;
    if length(v_out)>0 then v_out := v_out || ''')';
    else v_out := '''';
    end if;
    return v_out;
end;
K: Ugorjunk most a Pro*C-re: Igaz, hogy a NLS_LANG állása befolyásolja a preccompiler működését?
V: Hogyne, például ellenőrizheti a forrásprogramot, hogy UTF-8-valid-e, ha úgy áll a NLS_LANG. Ha ezt elkerülnéd, akkor pl. ilyesmit írj a Makefile-ba:
%.c: %.pc
    NLS_LANG=american_america.EE8ISO8859P2 proc code=ansi lines=yes iname=$< ...
K: Miért nem magyarra állítjuk a nyelvet?
V: Megtehetjük, ha fordító félreértéseire vagyunk kiváncsiak a program valódi üzenetei helyett...
K: És mit tegyek, ha Windowst használok?
V: Programozáshoz?!
K: Ha jól értem, OCI-t használva mezőnként állíthatom a karakterkészletet (konverziót). Pro*C-ben is van erre mód?
V: A lehetőségek egy kicsit szűkebbek: először is, az első adatbázis-műveletnél beolvassa a NLS_LANG és NLS_NCHAR környezeti változókat, és minden kontextusban azokat használja (még akkor is, ha új CONTEXT-et hozol létre).
K: Ez miért baj?
V: Baj csak akkor lesz, ha különböző komponenseket akarunk egy executable-programba összegyúrni, és mondjuk az egyik pl. EE8ISO8859P2/AL16UTF16 kódolást szeretne, a másik meg AL32UTF8/AL32UTF8-at.
K: Mi is lenne ez a két kódolás az előző válaszban?
V: Pro*C-ben a host-változók deklarációjától függ, hogy milyen kódolás menjen a dróton, illetve milyen kódolás legyen a változóban (ez a kettő persze különbözhet, a konverziót az Oracle kliensprogramja végzi). Megpróbálom egy táblázattal szemlélteni:
változódeklaráció                  dróton                  változó tartalma
-----------------		   ------		   ---------------
varchar				   NLS_CHARACTERSET        NLS_LANG
varchar CHARACTER SET IS NCHAR_CS  NLS_NCHAR_CHARACTERSET  NLS_NCHAR
uvarchar			   NLS_NCHAR_CHARACTERSET  UTF16
Megjegyzés: ha nincs NLS_NCHAR, akkor ahelyett is NLS_LANG érvényesül; ha nincs NLS_LANG, akkor 7-bites ASCII-t kapunk.
K: Az Oracle szerveren milyen karakter-konverzió történik?
V: Csak NLS_CHARACTERSET és NLS_NCHAR_CHARACTERSET közötti konverzió, ha szükség van rá (vagyis NCHAR/NVARCHAR/NCLOB mezőt a kliens "simán" akar kapni/küldeni; vagy fordítva, sima CHAR/VARCHAR/CLOB mezőt a kliens NCHAR-osan akar kapni/küldeni).
K: És ha ezen konverzió során karakterek vesznek el?
V: Akkor karakterek vesznek el.
K: És minden más konverzió a kliensen történik?
V: Igen.
K: És ha olyan szűkített képességű kliensem van, ami nem ismeri a szerver (egzotikus) kódkészletét, akkor mit tegyek?
V: Akkor azt nem tudod használni. Szerezzél egy okosabb klienst! (Még az InstantClient-nek is van mindenféle kódolást ismerő változata.)
K: Java-kliens (JDBC) esetén van valami különösebb aggódnivaló?
V: Például programfutáskor problémát okozhat, ha az orai18n.jar nem szerepel a classpath-on. Előfordulhat, hogy nincs hibaüzenet, "csak" hibás adatokat olvasunk ki az adatbázisból. Adott esetben elhelyezhetünk a 'main'-ben egy ilyen ellenőrzést:
if (oracle.jdbc.driver.OracleDriver.class.getClassLoader().
    getResource ("oracle/i18n/data/lx20020.glb") == null) {
    System.out.println ("Úgy érzem, hogy nincs meg az orai18n.jar -- kilépek");
    return;
}
K: JDBC-ről szólva, mi a különbség setString és setNString, illetve getString és getNString között?
V: Az előbbi esetben ettől függ, hogy a dróton NLS_CHARACTERSET vagy NLS_NCHAR_CHARACTERSET menjen-e; NCHAR-os mezők esetén az utóbbi ajánlott (az előbbi a szerver beállításától függően karaktervesztést okozhat, pl. EE8ISO8859P2-be nem férnek bele a cirill/görög betűk).
Az utóbbi esetben nem látok különbséget, ugyanis a getString/getNString akkor fut, amikor az adat már megérkezett a kliensre, ahol fixen UTF-16-ra konvertálódik.
K: JAVA/JDBC-esetén van-e valamilyen különleges szabály a NLS_LANG beállítására?
V: Ha méréseim nem csalnak, tetszés szerint beállíthatod, nem lesz hatása. Helyette a user.language és a user.country használható, vagyis azok alapján állítja be az Oracle a NLS_LANGUAGE és a NLS_TERRITORY értékét. Pl.:
java -Duser.language=hu -Duser.country=HU -Dfile.encoding=iso-8859-2 \
    -cp /opt/lib/java/ojdbc6.jar:/opt/lib/java/orai18n.jar: \
    ...
— ODBC —
K: ODBC-ről tudsz valamit mondani?
V: A unixODBC + Oracle kombinációt próbáltam, azt figyeltem meg, hogy a SQL_C_CHAR típus használata esetén a kódolás az NLS_LANG-nak megfelelően alakul, a SQL_C_WCHAR esetén pedig fixen UTF-16; ebből arra következtetek, hogy ha olyan ODBC-s programot akarnánk készíteni, amelynek a működése nem függ az éppen használt adatbáziskezelőtől (és annak beállításaitól), akkor az SQL_C_WCHAR-t érdemes használni.
— FPDF —

K: Mifene az az FPDF?
V: Egy PHP modul, PDF-ek előállítására szolgál.
K: Hogyan és hová kell telepíteni?
V: Erről nincs sok szó az install.txt-ben sem, a legjobb ötletem az, hogy a letöltött tgz/zip tartalmát helyezd el a /usr/local/share/php/fpdf könyvtárba (azon belül lesznek az fpdf.php, fpdf.css, stb. fájlok, és a doc, font, makefont, tutorial alkönyvtárak).

K: És a PHP meg fogja ott találni?
V: Az a php.ini fájl beállításaitól függ, pontosabban az include_path-tól; illetve használhatod a set_include_path függvényt is.
K: Egyéb tudnivaló a telepítésről?
V: Ha a PHP-d 5.3.x-es, vagy újabb, akkor távolítsd el a fpdf.php-ből a megszűnt get/set_magic_quotes_runtime hívásokat.

K: No igen, és hogy lesznek az ezzel előállított PDF-ben helyes magyar ékezetek?
V: Ahhoz, hogy a SetFont-tal kiválaszthass egy fontot, kell legyen egy megfelelő fontnév.php fájl a */fpdf/font könyvtárban. A gyárilag szállított fájlok cp1252-hez (vagyis nyugat-európai betűkhöz) vannak kialakítva.

K: És honnan lesznek a cp1250-es (kelet-európai betűs) fájlok?
V: Ehhez a mellékelt makefont.php-t kell használni, és kellenek hozzá a Windows-os ttf fájlok is. Példa:
FPDF=/usr/local/share/php/fpdf # vagy ahol van
WINFONT='/C/Windows/Fonts'     # vagy ahol vannak a fontok
for FONT in arial ariali arialbd arialbi; do
    php $FPDF/makefont/makefont.php "$WINFONT/$FONT.ttf" cp1250 false
done
K: Ugyanez Windows-on hogy lenne?
V: Értelmszerűen módosítani kell a scriptet (vagyis batch-et, Windows-os szóval), valami ilyesmi lehetne:
@echo off

set FPDF=C:\PHP-EXT\FPDF
set WINFONT=C:\Windows\Fonts

cd %FPDF%\font
mkdir cp1250 2>nul:
cd cp1250
for %%I in (%WINFONT%\*.ttf) do php %FPDF%/makefont/makefont.php %%I cp1250 false
Megj: További Windows-os információkért lapozz vissza ide.
K: Ezt minden futásnál újra meg újra meg kell csinálni?
V: Nem, a fájlok újrahasznosíthatóak; több megoldás képzelhető el, az egyik ez: az fpdf font-könyvtárában készítsünk egy cp1250 nevű alkönyvtárat, és oda helyezzük el a létrehozott *.php fájlokat.
FPDF=/usr/local/share/php/fpdf	# vagy ahol van
WINFONT='/C/Windows/Fonts'	# vagy ahol vannak a fontok
cd $FPDF/font
mkdir cp1250
cd cp1250
for i in "$WINFONT"/*.ttf; do	# vagy nagybetűvel: *.TTF
    php $FPDF/makefont/makefont.php "$i" cp1250 false
done
K: És a SetFont automatikusan az új fájlokat fogja használni?
V: Nem, ehhez kell még egy AddFont is. Komplett példa:
#!/usr/local/bin/php
<?php /* fpdf-test.php encoding="iso-8859-2" */

    require_once ('fpdf/fpdf.php');

    $pdf = new FPDF();
    $pdf->AddPage();
    $pdf->AddFont('Arial','B','cp1250/arialbd.php');
    $pdf->SetFont('Arial','B',16);
    $pdf->Cell(40,10,'árvíztűrő tükörfúrógép');
    $pdf->Output('fpdf-test.pdf', 'F');
?>
K: Ez volt a nem-beágyazott font, ugye? Mutatnál példát a beágyazottra is?
V: Tegyük fel, hogy a csodas.ttf tartalmazza a CsodaSzep fontot (kerülném a konkrét neveket, mivel valaki más fontjának beágyazása esetleg jogsértés is lehet), ekkor valami ilyesmit tehetünk (ismétlem, a többféle lehetséges megoldás közül ez csak az egyik):
FPDF=/usr/local/share/php/fpdf # vagy ahol van
$FPDF/makefont/makefont.php csodas.ttf cp1250 true
cp csodas.php $FPDF/font/cp1250/
cp csodas.z   $FPDF/font/cp1250/
Ezután a programban ugyanúgy használhatjuk:
    $pdf->AddFont('CsodaSzep','','cp1250/csodas.php');
    $pdf->SetFont('CsodaSzep','',16);

K: Miért épp cp1250 és nem iso-8859-2?
V: A leírt módszerrel mindkettőt megcsinálhatod (külön alkönyvtárakba), nem fogják zavarni egymást (ugyanebben a FAQ-ban találsz infót arról, hogy mi a két kódkészlet között a különbség).

— TCPDF —

K: És a TCPDF is valami hasonló?
V: Igen, van hozzá leírás példákkal; részletesebb leírás magában a tcpdf.php fájlban van, kommentek formájában.

K: Hogyan telepítsem?
V: Az egyik lehetőség, hogy elolvasod a leírást, a másik, amit én alkalmaztam, hogy a letölthető .zip fájl tartalmát a /usr/local/share/php/tcpdf könyvtárba helyeztem, utána futtattam a chmod -R g-w,o-w /usr/local/share/php/tcpdf parancsot, mivel a jogbitek egy kicsit ijesztőek voltak (world-writeable fájlok).
Érdemes gondoskodni arról is, hogy a php.ini-ben az include_path tartalmazza a /usr/local/share/php -t (ennek részleteit lásd az FPDF-nél).

K: Ez ugyebár UTF-8 kompatibilis, és egy csomó beépített fontja van... Akkor ezzel nyilván nagyon könnyű magyar nyelvű PDF-et csinálni.
V: Nagyon, de ha mégis kérdőjel jelenne meg az 'ő' és 'ű' helyén egyes fontoknál; illetve ha olyan fontokat is használni akarsz, amik nincsenek benne gyárilag, akkor azért van egy kis gond. Ilyenkor egy pici programot futtatunk, amely a gépünkön meglévő 'jó-ékezetű' TTF fájlokból az AddTTFFont függvénnyel létrehozza az új/javított font-leíró fájlokat a tcpdf/fonts könyvtárban.
$ cd /tmp
$ cat >tcpdf-import.php <<DONE
#!/usr/local/bin/php
<?php /* tcpdf-import.php */

    require_once ('tcpdf/tcpdf.php');

    $pdf = new TCPDF (PDF_PAGE_ORIENTATION, PDF_UNIT, PDF_PAGE_FORMAT, true, 'UTF-8', false);

    for ($i=1; $i<$argc; ++$i) {
	$fontname= $pdf->AddTTFFont ($argv[$i], 'TrueTypeUnicode', 'UTF-8');
	printf ("from file '%s' font '%s' created\n", $argv[$i], $fontname);
    }
    $pdf->Close ();
?>
DONE
$ su # root-ként tudjuk írni a tcpdf/fonts könyvtárat
# TCPDF='/usr/local/share/php/tcpdf' # vagy ahol van
# WINFONT='/C/WINDOWS/Fonts' # vagy ahol van
# for font in trebuch times arial; do # vagy amit jónak látunk
#     rm $TCPDF/fonts/$font*php # hajlamos nem-felülírni a meglévőt
#     php tcpdf-import.php "$WINFONT/"*.ttf
# done
# ls -ltr $TCPDF/fonts # a végén kell lássuk a szép új fájlokat
K: Egy tesztprogramot kaphatok?
V: Véletlenül épp csináltam egyet (fájlba ír):
#!/usr/local/bin/php
<?php /* tcpdf-test.php encoding="iso-8859-2" */

    require_once ('tcpdf/tcpdf.php');

    $pdf = new TCPDF (PDF_PAGE_ORIENTATION, PDF_UNIT, PDF_PAGE_FORMAT, true, 'UTF-8', false);
    $pdf->AddPage ();

    $txt= 'Árvíztűrő tükörfúrógép';
    $utxt= iconv ('ISO-8859-2', 'UTF-8', $txt); /* nem kell, ha eleve UTF-8-ban vagyunk */

    $pdf->SetFont ('times', 'BI', 20);
    $pdf->Write (0, $utxt);

    $pdf->SetFont ('trebuc', 'BI', 20);
    $pdf->Write (0, $utxt);

    $pdf->SetFont ('arial', 'BI', 20);
    $pdf->Write (0, $utxt);

    // close and write to file
    $pdf->Output ('test.pdf', 'F');
?>
— HTML2PDF —
K: Ha jól értem, a html2pdf is a TCPDF-et használja.
V: Úgy látom én is. A telepítését is ahhoz hasonlóan végeztem: unzip a /usr/local/share/php/html2pdf-be. Most jön egy érdekes lépés: a beledolgozott TCPDF-et töröljük, és helyettesítjük a saját, kijavított fontú példányunkkal:
# cd /usr/local/share/php
# ls -ld html2pdf/_tcpdf_5.0.002 tcpdf
drwxr-xr-x 8 root root 4096 May 27  2011 html2pdf/_tcpdf_5.0.002 # ezt fogjuk helyettesíteni
drwxr-xr-x 8 root root 4096 Mar  4 21:57 tcpdf			 # ezzel
# mv html2pdf/_tcpdf_5.0.002 html2pdf/orig_tcpdf_5.0.002
# ln -s ../tcpdf html2pdf/_tcpdf_5.0.002
K: És a tesztprogram?
V: Itten van; ha kipróbálod, láthatod, hogy az Arial nem jól jelenik meg, mivel azt belsőleg valamilyen lelki okból Helveticá-ra cseréli, amiben nem jó a magyar 'ű' és 'ő'.
#!/usr/local/bin/php
<?php /* tcpdf-test.php encoding="iso-8859-2" */

    $kPathUrl = 'No random notices, pls';
    require_once ('html2pdf/html2pdf.class.php');

    $content = "<page>\n".
        "<h1>Árvíztűrő tükörfúrógép</h1>\n".
        "<p style=\"font-family: times;\">Öt szép szűzlány őrült írót nyúz.</p>\n".
        "<p style=\"font-family: trebuc;\">Öt szép szűzlány őrült írót nyúz.</p>\n".
        "<p style=\"font-family: arial;\">Öt szép szűzlány őrült írót nyúz.</p>\n".
        "</page>\n";
    $ucontent= iconv ('ISO-8859-2', 'UTF-8', $content);
    /* nem kell, ha eleve UTF-8-ban vagyunk */

    $html2pdf = new HTML2PDF ('P', 'A4', 'en');
    $html2pdf->WriteHTML ($ucontent);

    $html2pdf->Output ('html2pdf-test.pdf', 'F');
?>
K: És ha mégis szeretném, hogy az Arial megmaradjon Arialnak?
V: Ezt a patch-et próbáld ki:
--- html2pdf/_class/parsingCss.class.orig       2011-05-26 18:01:10.000000000 +0200
+++ html2pdf/_class/parsingCss.class.php        2013-04-11 12:42:33.000000000 +0200
@@ -318,3 +318 @@
-            if($family=='arial')
-                $family='helvetica';
-            elseif($family=='symbol' || $family=='zapfdingbats')
+            if($family=='symbol' || $family=='zapfdingbats')
@@ -328,3 +326 @@
-        if($family=='arial')
-            $family='helvetica';
-        elseif($family=='symbol' || $family=='zapfdingbats')
+       if($family=='symbol' || $family=='zapfdingbats')
— E-mail —
K: Ha egy email fejrészében ékezetes betűk vannak, az hogyan továbbítódik?
V: Hét bites ASCII-re kódolva az RFC2047-ban leírt módszerrel. Példa magyar szövegek és ISO-8859-2 használatával:
ezt látod:
     To: Őrült Író <lzsiga@domain.hu>
Subject: Árvíztűrő tükörfúrógép
ez van mögötte:
     To: =?ISO-8859-2?Q?=D5r=FClt_=CDr=F3?= <lzsiga@domain.hu>
Subject: =?ISO-8859-2?Q?=C1rv=EDzt=FBr=F5_t=FCk=F6rf=FAr=F3g=E9p?=
K: Ez a levél törzsére is vonatkozik?
V: Nem, a levél törzse a fejrészben lévő Content-Type és Content-Transfer-Encoding értékektől függ. ISO-8859-2 használata esetén a legtisztább eset ez:
Content-Type: text/plain; charset=ISO-8859-2
Content-Transfer-Encoding: 8bit
Ekkor az üzenetben az ékezetes betűk úgy mennek át, ahogy megjelennek. Ennél csúnyább a következő megoldás:
Content-Type: text/plain; charset=ISO-8859-2
Content-Transfer-Encoding: quoted-printable
a levétörzsben ezt látod:
Kérjük tájékoztass, hogy a szolgáltatás működőképes-e.
ez van mögötte:
K=E9rj=FCk t=E1j=E9koztass, hogy a szolg=E1ltat=E1s m=FBk=F6d=F5k=E9pes-e.
Ennél már csak az csúnyább, ha a bináris fájlok átvitelére való base64-kódolást használjuk szövegekre:
Content-Type: text/plain; charset=ISO-8859-2
Content-Transfer-Encoding: base64
Ebben az esetben a tartalom (vagyis a levél "forráskódja") olvashatatlan – ez persze a hétköznapi felhasználásban nem probléma, hiszen a levelezőprogram tökéletesen eligazodik a különféle formátumok között, de ha valamiért saját programból akarjuk kezelni a levelet, fel kell készülnünk a levéltörzs különböző lehetséges formáinak kezelésére a Content-Transfer-Encoding függvényében.

K: HTML (vagy PDF/GIF/etc) formátumú levelet csináltam, tök szép is lett, de valaki (pl. a spam-szűrő), azt mondja, hogy jobb lenne, ha plain-text-ben is meglenne ugyanaz. Ez miért lenne jó?
V: Egyrészt azoknak kedvezne, akik csak szöveges formátumot olvasnak, vagy valamilyen digest-formában olvassák a levelet, esetleg nem (jól) látnak, és egy program olvassa fel nekik a leveleket; másrészt megbékítené a spamszűrőt.
K: És akkor kétszer jelenik meg a levél szövege a címzettnél?
V: Szó sincs róla, azért hívják ezt multipart/alternatives-nek, hogy jelezzék, ezek egymás alternatívái, csak az egyiket kell megjeleníteni.

— SMS —
K: 7-bites, 160-karakteres 'ékezetnélküli' üzemmódban milyen ékezetes betűket használhatok?
V: Ez a wiki oldal írja le, kiemelem belőle a nekünk fontos betűket:
    SMS-kód karakter unikód/latin1
    05      é	     e9
    06      ù	     f9
    07      ì	     ec
    08      ò	     f2
    1f      É	     c9
    5b      Ä	     c4
    5c      Ö	     d6
    5e      Ü	     dc
    7b      ä	     e4
    7c      ö	     f6
    7e      ü	     fc
    7f      à	     e0 
K: 8-bites, 140-karakteres üzemmódban milyen ékezetes betűket használhatok?
V: Fogalmam sincs, nincs olyan készülékem, amivel ilyet lehetne küldeni.
K: 16-bites, 70-karakteres üzemmódban milyen ékezetes betűket használhatok?
V: Ez megfelel az UCS2-nek, tehát minden magyar betű benne van (meg minden más karakter is, aminek az unikódja elfér 16 biten... persze az nem garantált, hogy a címzett készüléke mindet meg is tudja jeleníteni).
— Samba —
K: A Samba-nak is van köze ehhez a FAQ-hoz?
V: Bizonyos értelmeben igen: az én esetemben a /etc/passwd-ben volt ékezetes magyar szöveg, latin2-es kódolással, ami megakadályozta a Samba-4.4.2 elindulását. Az egyik lehetőség a fájl utf8-asítása lett volna, a másik a smb.conf módosítása az alábbiak szerint:
unix charset = iso-8859-2
dos charset = cp852
(ebből az első sor a lényeges)
K: Ha már itt tartunk, mi a legfontosabb tudnivaló az ékezetes fájl- és könyvtárnevekkel kapcsolatban?
V: Hát az, hogy ne használjunk ilyeneket. Ugyanez vonatkozik a szóközökre és speciális jelekre. (Ha valaki azzal állna elő, hogy a fájlnévben kell as ékezet/szóköz, mert az jelenik meg a felhasználó böngészőjében, akkor magyarázzuk el neki, hogy a fájlnév egyáltalán nem arra való, hogy megjelenjen a felhasználó böngészőjében.
— RSA-kulcspár —
K: Ez a rész vajon hogy került ide?!
V: Fogalmam sincs. Lehet, hogy valaki valamit rosszul csinált.
K: Hogyan generálok RSA-kulcspárt az OpenSSL programmal?
V: Két lépésben, az első a privát kulcsot hozza létre, a második abból a publikust (látszik, hogy a kettő nem szimmetrikus, a privát több információt tartalmaz, mint a publikus)
openssl genrsa -out hugo.rsaprv.key 2048
openssl rsa -in hugo.rsaprv.key -RSAPublicKey_out -out hugo.rsapub.key
openssl rsa -in hugo.rsaprv.key -pubout -out hugo.pub.key
K: Ez nem három lépés volt egészen véletlenül?
V: Hát igen, úgy tűnik, hogy a publikus kulcsnak két formája is lehetséges; egyes programok esetleg az egyiket szeretik, mások a másikat. Maga az openssl a 'pub'-ot látszik szeretni (csak azzal működik a következő):
echo 'Malacvacsora' |\
openssl rsautl -encrypt -out titkos -pubin -inkey hugo.pub.key
openssl rsautl -decrypt -in titkos -inkey hugo.rsaprv.key
Malacvacsora
Másik példa:
openssl pkey -pubin -in hugo.rsapub.key -text
unable to load Public Key
openssl pkey -pubin -in hugo.pub.key -text
(kiírja a kulcs adattartalmát)
Megj: Tudományosan mondva az 'rsapub' PKCS#1 formában van, a 'pub' pedig PKCS#8 formában.
K: Ez a két változat egymásból is előállítható?
V: Valami ilyesmi lenne:
openssl rsa -pubin -in hugo.pub.key -RSAPublicKey_out -out test.rsapub.key
openssl rsa -RSAPublicKey_in -in hugo.rsapub.key -pubout -out test.pub.key
K: Szemmel belenézve úgy látom, hogy különbözik a szöveges fejrész, illetve a 'pub' változatban az adattartalom hosszabb, mint a 'rsapub' változatban.
V: Ez így van, sőt a hosszabb változat a rövidebbtől csak egy fejrészben különbözik, ami (legalábbis az én esetemben) az alábbi 32 byte (base64 nélkül 24 byte):
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8A
K: Ha történetesen C-programból szeretnék kulcspárt generálni, milyen függvényeket használhatok?
V: Ezeket javasolnám: RSA_generate_key, i2d_RSAPrivateKey ('prv'-hez), i2d_RSAPublicKey ('rsapub'-hoz), i2d_RSA_PUBKEY ('pub'-hoz). Az utóbbi három leírása itt olvasható.
— Makefile —
K: Off-topik, de hátha elfér: egy Makefile-ban hogy lehet szóközökkel kavarni, különös tekintettel arra, hogy a stringeket ott nem teszük idézőjelek közé?
V: Egy szóközt tartalmazó változót lehet használni erre a célra; kicsit mesterkélt megoldás, de működik.
Az alábbi példában a 'CLASSPATH' változó értékét folytatósorok használatával adjuk meg, ami viszont szóközöket iktat be, tehát azoktól meg kell szabadulni a 'subst' függvénnyel:
CLASSPATH := \
    /itt/elso.jar:\
    /ott/masodik.jar:\
    /amott/harmadik.jar

EMPTY :=
SPACE := ${EMPTY} ${EMPTY}

CLASSPATH := $(subst ${SPACE},,${CLASSPATH})

run:	main.class
	java -cp=${CLASSPATH} main.class
— Hibakeresés —

K: Ha kérdőjeleket vagy krixkraxokat látok a magyar ékezetes betűk helyett, hogyan debuggoljak?
V: Például mentsd le fájlba (wget), és egy hex-viewerrel ellenőrizd, hogy mi is van ott (ezen lap alján van egy táblázat, ami segíthet). És ha már a wget-et használod, a -S opciót is használd, amit már az előbb ajánlottam, hogy a HTTP-fejrészt is lássad.

K: További ötlet?
V: Próbáld megállapítni, hogy minden ékezetes betű egyformán rossz-e, vagy másképp rosszak/jók attól függően, hogy mi a forrásuk:

K: Már összevissza kapkodtam, mint majom a farkához, de bármit is állítgatok, valami mindig elromlik: vagy a html, vagy a php, vagy az adatbázisból jövő szöveg lesz rossz...
V: Akkor talán csináld sorrendben: először a konstans fájlok (html, txt, js, css...) tartalma jelenjen meg jól, azután a php-ból/cgi-ből származó tartalom, és végül az, ami az adatbázisból jön.

K: Arra gondoltam, hogy egy internetes levlistán vagy fórumon kérek segítséget. Az vajon jó lesz, hogy 'Nem mennek az ékezetek a szájtomon, segítsetek!'
V: Szinte tökéletes, de ha egészen profi akarsz lenni, akkor valahogy így fogalmazz (a változó részeket értelemszerűen behelyettesítve):
'Hosszas megfontolás után elhatároztam, hogy az <X> kódolást fogom használni, ehhez állítottam a .htaccess-t, a html.head.meta-t, a header-t, az adatbázist, és még mindig gondom van.
Tesztelésiből 'wget -S'-sel letöltöttem az oldalam, a Content-Type fejrész <jó/rossz>; az elmentett tartalomban hex-viewerrel megnézve az ékezetes betűk kódját, azok <olyanok/nem olyanok>, mint amit várok (ha nem olyanok, akkor milyenek: <XXH>-et vártam az <Ű> helyett, jött helyette <YYH>, lásd a függelékben a táblázatot).'

K: Az oldalam az X gépen jól működik, az Y gépen meg nem. Mi lehet a baj az Y géppel?
V: Nyomozd ki! Pont arról szól ez a 'Hibakeresés' rész.
K: Mindenestre az biztos, hogy az oldalam jó, hiszen az X gépen jól jelenik meg!
V: Ez tévedés, a hibás működés bizonyítja a hibát, de a hibátlan működés nem bizonyít semmit, lehetséges például, hogy a hibák semlegesítik egymást. Szóval: debuggolj!

K: Ha nem vagyok biztos abban, hogy pontosan mi van az adatbázisomban, hogyan kérdezhetem le a hexakódokat?
V: Az adatbáziskezelődtől függ, pl.:
MySql:  SELECT HEX (mezonev) FROM ...
Oracle: SELECT DUMP (mezonev, 16) FROM ...
  vagy: SELECT RAWTOHEX (mezonev) FROM ...
Pgsql:  SELECT ENCODE (mezonev, 'hex') FROM ...
K: PHP-ban hogyan lehet debuggolni?
V: Főleg teszt-kiíratásokkal, lásd print, printf, print_r, var_dump, illetve, ha nem látszik a kimenet (pl. Ajax esetén), akkor fájlba: error_log. Hasznos lehet a hexás kiíratás is a bin2hex függvénnyel, pl.:
    $s= $_REQUEST['input_mezo'];
    printf ("bin2hex(%s)=%s\n", $s, bin2hex ($s));

    $s= iconv ('ISO-8859-2', 'UTF-8', $s);
    printf ("utf8-ra kódolva: bin2hex(%s)=%s\n", $s, bin2hex ($s));
K: Ha kiírattam a HTML-ben, és láttam, hogy jó, akkor...?
V: Az nem feltétlenül elég, jobb lenne hexásan is megnézni. Ha mondjuk az van ott, hogy &#x151;, az a böngészőben úgy fog megjelenni, mint ő, de még sokkal jobb, ha ténylegesen az van ott, hogy ő.
K: Miért is? Hiszen úgyis csak HTML-hez kell, akkor meg mindegy, nem?
V: Nem, mert lehet, hogy holnap már adatbázisba akarod írni, és keresni akarsz benne... holnapután meg esetleg egy text-fájlt kell előállítani, ahol az &-szekvenciák nem használhatóak.
K: Hibakeresésről szólva, melyek azok a hibák, amelyeket a hagyományok szerint minden kezdő köteles újra meg újra elkövetni?
V: Itt van rögtön a warningok figyelmen kívül hagyása. Néhány példa, néhány programozási nyelvhez / fordítóhoz, amit csinálni kellene:
C/C++ fordítás gcc-vel: gcc -W -Wall -Wextra -pedantic ...
PHP script elejére: error_reporting (E_ALL | E_STRICT);
Perl futtatáshoz: perl -w script
Perl script elejére: use strict;
K: Az is ide tartozik, ha az egyes adatbázisműveletek után 'elfelejtem' a hibaellenőrzést? Vagy beteszem, de csak annyit írok ki, hogy 'baj van'?
V: Nagyon is jó, még elfáradnál! Két példa PHP/MySql-ból egy kezdős, meg egy haladós.
$stmt = mysql_query ("SELECT fields FROM table WHERE field='$valtozo'");
while ($row= mysql_fetch_row ($stmt)) ...
$sql = sprintf ("SELECT fields FROM table WHERE field='%s'",
    mysql_real_escape_string ($valtozo));
$stmt = mysql_query ($sql);
if ($stmt === FALSE) {
    printf ("<H1>scriptneve.rutinneve: MySql error %s on %s</H1>\n",
	htmlspecialchars (mysql_error (), ENT_NOQUOTES, 'ISO-8859-1'),
	htmlspecialchars ($sql, ENT_NOQUOTES, 'ISO-8859-1'));
    exit;
}
while ($row= mysql_fetch_row ($stmt)) ...
Kieg: További hasznos függvények: mysql_affected_rows – hány sort érintett a legutóbi módosítás (pl UPDATE), mysql_insert_id – ha a legutóbbi parancs INSERT volt, és abban egy AUTO_INCREMENT típusú kulcs töltődött, annak értékét adja vissza.
Kieg: Itt van Oracle-hez egy minta a hibavizsgálatra.
K: További lehetőségek?
V: Például, ha azt hajtogatod, hogy 'nem érdekelnek a részletek, csak azt szeretném, hogy jó legyen'. Telitalálat, ha bosszantani akarod a potenciális segítőket. Egy másik ilyen, hogy ha valaki javasol valamit, azt feleled: 'kipróbáltam, nem vált be'.
K: De ha tényleg nem vált be?
V: Akkor idézd be a kódot, amit próbáltál (pontosan, nem emlékezetből) és a hibaüzenetet/hibajelenséget. És ha egy mód van rá, a hibaüzenetek nyelvét állítsd angolra, mert nem a fordító félreértéseire vagyunk kiváncsiak, hanem az igazi hibaüzenetre.
K: Én aztán nagyon szívesen beidézem a programomat, mind a sokezer sort...
V: Legyél szíves előzőleg mindent kiszedni belőle, ami nem nélkülözhetetlen a probléma bemutatásához; viszont ami marad, az legyen komplett, fordítható, tesztelhető.
K: Dehát ez sok munkával járna...
V: Vagy pedig fizess meg egy programozót.
Függelék
A magyar betűk kódja néhány kódkészletben:
+------+----------+----------+--------------+--------------+
| Betű |    852   |  Latin2  |   Unicode    |     UTF-8    |
+------+----------+----------+--------------+--------------+
| á  Á |  A0  B5  |  E1  C1  |    E1    C1  |  C3A1  C381  |
| é  É |  82  90  |  E9  C9  |    E9    C9  |  C3A9  C389  |
| í  Í |  A1  D6  |  ED  CD  |    ED    CD  |  C3AD  C38D  |
| ó  Ó |  A2  E0  |  F3  D3  |    F3    D3  |  C3B3  C393  |
| ö  Ö |  94  99  |  F6  D6  |    F6    D6  |  C3B6  C396  |
| ő  Ő |  8B  8A  |  F5  D5  |  0151  0150  |  C591  C590  |
| ú  Ú |  A3  E9  |  FA  DA  |    FA    DA  |  C3BA  C39A  |
| ü  Ü |  81  9A  |  FC  DC  |    FC    DC  |  C3BC  C39C  |
| ű  Ű |  FB  EB  |  FB  DB  |  0171  0170  |  C5B1  C5B0  |
| ä  Ä |  84  8E  |  E4  C4  |    E4    C4  |  C3A4  C384  |
+------+----------+----------+--------------+--------------+

Kiegészítés: Egy nem-magyar betű:

+------+----------+----------+--------------+--------------+
| Betű |    852   |  Latin2  |   Unicode    |     UTF-8    |
+------+----------+----------+--------------+--------------+
| ô  Ô |  93  E2  |  F4  D4  |    F4    D4  |  C3B4  C394  |
+------+----------+----------+--------------+--------------+

Kiegészítés: Még két nem-magyar betű:

+------+----------+----------+--------------+--------------+
| Betű |    850   |  Latin1  |   Unicode    |     UTF-8    |
+------+----------+----------+--------------+--------------+
| õ  Õ |  E4  E5  |  F5  D5  |    F5   D5   |  C3B5  C395  |
| û  Û |  96  EA  |  FB  DB  |    FB   DB   |  C3BB  C39B  |
+------+----------+----------+--------------+--------------+
További olvasmányok
Off-topik, de hasznos olvasmányok