É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=UTF-8
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:
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 FirefoxLiveHttpHeaders
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:
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:
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):
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:
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: A fentebb említett %-szekvenciákat milyen szabvány definiálja? V: Az RFC3986 2.1-es fejezete
írja la a Percent-Encoding-ot; a legfontosabb tudnivaló, hogy a %-szekvencia mindig három
karakterből áll (%<hexdig1><hexdig2>) és egyetlen bájtot
reprezentál. A bájt értelmezése ASCII-kompatibilis kell legyen (pl. ISO-8859-x, UTF-8).
Nem kell (bár megengedett) %-szekvenciával ábrázolni az a-zA-Z0-9~_-.
karaktereket, minden mást igen. (Ez azért kontextustól függ, lásd például lentebb a wget-nél.)
Speciális eset a szóköz (ASCII 0x20): egyes változatok a %20
szekvencia helyett/mellett a + jelet használják a szóköz ábrázolására.
K: Tehát a %-szekvenciáknál nincs előírva, hogy mondjuk UTF8-at
vagy ISO-8859-2-t használjak? V: Így van, az RFC csak ASCII-karakterekkel foglalkozik, tehát arról nem mond semmit, hogy
például valamely kontextusban a %C3%95 UTF8-szerint egy darab
Õ karaktert jelent-e, vagy két karaktert (és az utóbbi esetben
milyen kódolás szerint).
K: HTTP protokoll esetén hol találkozom én ilyen %-szekvenciákkal? V: Az URL-fájl/path részében (a példát lásd fentebb); a GET-paraméterben
(A ? utáni rész, más néven: Querystring);
valamint a HTTP-Body-ban, ha a Content-Type értéke
application/x-www-form-urlencoded.
K: Nézzük konkrétan a PHP esetét: a GET/POST adatban lévő
%-szekvenciákat milyen kódolás szerint értelmezi a PHP-motor? V: A PHP-string típusa bájtok sorozata, tehát a felhasználói program feladata
a bájtok értelmezése.
K: Akkor nézzük fordítva: a kliensprogram (böngésző)
milyen kódolás szerint állítja elő a %-szekvenciákat a GET/POST adatmezőiben? V: Általálában az illető FORM-ot tartalmazó oldal
kódolásának megfelelően, de a
FORM-nál is megadhatunk egy
accept-charset
értéket.
K: A wget-ről
mit mondhatunk? V: A következő információtöredékeket szedtem össze:
A --post-data és --post-file tartalmát
nem módosítja.
Az URL-t (és benne a Querystring-et) urlencode-olja (nem minden karaktert), pl.:
Ezt a kódkonverziót letilthatjuk a --no-iri opcióval.
A konverzió kimeneti kódolása mindig UTF-8; a bemeneti kódolása a
--local-encoding opció értéke, ennek hijján a
LC_CTYPE (ha a bemeneti kódolás UTF-8, akkor nem történik konverzió).
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 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 Õ és Û 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:
Ő=Ő, ő=ő, Ű=Ű, ű=ű.
K: Hol találok egy listát az összes ilyen &-szekvenciáról,
hogy pl.: …=… vagy β=β? 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.: Γ=Γ=Γ=Γ.
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):
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átjukezt 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 egyedi 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 egyedi 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 🍔=🍔=🍔 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:
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:
K: Mik lennének ezek a négybájtos
(avagy kétszer kétbájtos) szekvenciák? 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á feltüntetem az UTF8-as és a CESU8-beli kódot is):
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:
a U+7F fölötti unikódokat 2-4 bájtos szekvencia reprezentálja
ezeknek a szekvenciáknak az első bájtja 0xC0 és 0xF4 között van,
a többi bájtja 0x80 és 0xBF között
a szekvencia hosszát az első bájtból lehet meghatározni
hogy a kódolás egyértelmű legyen, hibásnak kell tekinteni az olyan UTF-8 szekvenciát,
ami nem a lehető legrövidebb (pl. 2-4 bájton ábrázolt ASCII karakter nem fogadható el)
hibásnak kell tekinteni az olyan szekvenciákat is, amik U+10FFFF feletti unikódot ábrázolnak
(a legnagyobb legális érték: F4 8F BF BF)
a szabvány szerint hibásnak kell tekinteni az olyan szekvenciákat is,
amik az U+D800..U+DFFF tartománynak felelnek meg,
mivel ezek nem reprezentálnak karaktert (lásd fentebb),
CESU-8-ban viszont ezek megengedettek
ha BOM-ot akarunk tenni elé, akkor az EF BB BF legyen (ami az U+FEFF kódja)
Megj.: Itt a 'minimum' nem azt jelenti, hogy annál kisebb értéket ne
lehetne ábrázolni az adott bájtsorozattal, hanem azt, hogy a szabvány szerint
hibásnak kell tekinti ilyen bájtsorozatot, amelyik a minimumnál kisebb értéket repretentál.
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
:
Megj.: a V+szám jelölést csak ennek a példának a kedvéért találtam ki,
hogy hangsúlyozzam, hogy a jelenlegi szabvány szerint nem lehet ilyen unikód.
K: Még további UTF-8 változatok is vannak? V: Van még egy UTF-8-MOD nevű speciális változat,
amit EBCDIC-kel kapcsolatban szokás használni:
UTF-EBCDIC (avagy UTFE):
Az így kapott bájtsorozatokat úgy kell az adott EBCDIC-változatba konvertálni,
mintha ISO-8859-1 kódban lennének. (Érdemes tudni, hogy az EBCDIC-nek számos
inkompatibilis változata van.)
Ennek a módszernek az előnye az, hogy a 0x80..0x9f értekek
(binárisan 100xxxxx), amelyek az ISO-8859-es kódolásban
C1 vezérlőkaraktereket
jelentenek nem szerepelnek a több-bájtos szekvenciákban. K: Ez miért lenne jó? V: A hagyományos egybájtos kódolásra felkészített programok ezeket
a bájtokat vezérlőkarakternek tekinthetnék, például az U+85 kódú NL
(vagy NEL) – illetve annak EBCDIC-megfelelője –
soremelést
jelent(het). K: És ez mitől EBCDIC-specifikus? V: Igazából nem az, de ASCII-kiterjesztésből sok van, és nem mindegyik
tekinti vezérlőkarakternek ezeket a kódokat (lásd pl ISO8859-2 vs windows-1252),
tehát kevésbé valószínű, hogy a programok működését zavarnák ezek a kódok.
Azonkívül az EBCDIC-et használó mainframe-eken gyakori, hogy bevált programokat
változatlanul futtatnak évtizedeken át, ezért itt a kompatibilitási szempontok
fontosabbak. K: Az UTF-8-nál említetted, hogy hosszabb szekvenciákkal több karaktert is lehet(ne)
ábrázolni (ha az Unicode értéktartományát nem csökkentették volna).
Ez itt is lehetséges? V: Igen, valahogy így:
K: Már voltszó
a CESU-8-ról, de mi is pontosan? V: Az UTF-8 változata vagy rokona;
azzal azonos a U+0..U+d7ff és U+e000..U+ffff tartományokon
(emlékezzünk, d800 és dfff között nincsenek karakterkódok); az afölötti kódokat az UTF-16-nál
leírt helyettesítő párok UTF-8-as kódolásával alakítja hatbájtos szekvenciává, pl.:
K: Nagyon szép ez a hatbájtos szekvencia, de miért vetted elő ezt a
recode nevű programot? Ha jól látom, ugyanarra való,
mint az iconv (csak más szintaktikával). V: Az egyik gond az, hogy a kettő egyike sem ismeri a CESU-8-at (ezért kell
két lépésben konvertálni),
a másik az, hogy (ahogy fentebb mondtuk) a d800..dfff tartomány "tiltott",
ezért az iconv nem is hajlandó velük foglalkozni,
a recode viszont "engedékenyebb".
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:
Megjegyzés: Láthatjuk, hogy a wchar_t típusú string-ben lehet
\x,
\u és \U
szekvenciákat használni, mindegyik Unicode-ot jelent, csak a használható
számjegyek száma különbözik: 2, 4, 8. (Igazából a \x
a mögötte álló összes hexadecimális számjegyet felhasználja,
pl "\x17ez" == "\x17e"+"z", ezért használatához
óvatosság ajánlott.)
K:
De ha egyszerűen L'ä' vagy
L"öőüű" formában akarom rögzíteni ezeket a 'wide'stringeket,
abben 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 ha utf8-at nevezünk tévedésből latin2-nek, azt nem veszi észre.)
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.
K: És milyen függvényekkel konvertálhatok Windows-ban "keskeny" és "széles" között? V: Ezeket használhatod:
MultiByteToWideCharWideCharToMultiByte.
Nevükkel ellentétben nem csak multibájtró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 a literal tipusának megfelelően konvertálódik
UTF16-re, UTF32-re vagy UTF8-ra (lásd a következő pontban az
L",
u",
U" és u8" tipusjelzőket);
\x szekvenciáknál nincs konverzió.
A \u és \U szekvenciákat
(legalábbis egy fordítókkal, pl. gcc) nem használhatjuk "túl kicsi" értékek
megadására (pl. U+a0 alatti értékek), illetve a \u
szekenciával nem adhatunk meg surrogate pair-t sem. Pl.:
char uint16_t eznemjo[]= u"\u0020\ud83c\udf54"
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:
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):
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:
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
K: A Microsoft MSC
compiler esetén milyen opciókat használhatok erre? V: A forrásprogram kódolását a
/source-charset
opcióval állíthatjuk be, a 'kimenő' kódolást pedig
/execution-charset
opcióval, illetve van egy összevont
/utf-8
opció is, ami mindkettőt UTF-8-ra állítja.
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:
Rengeteg nulla bájt van benne? ha igen, akkor UTF32.
Sok nulla bájt van benne? Ha igen, akkor UTF16.
UTF8-ként értelmezhető? Ha igen, akkor UTF8.
Egyébként valamilyen egybájtos kódolás (ezt jelenti az 'ANSI').
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.)
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.TXTIBM-1047.TXT
K: Az iconv-on kívül milyen segédprogramokat
használhatunk konverziós feladatokhoz?
V: Az iconv-hoz hasonló a
recode, hívási példa:
Sajnos az ascii2uni nem hibátlan a \u szekvenciák kezelésében
(egy kísérlet a javításra
itt látható),
helyette a Java
native2ascii
nevű segéprogramját ajánlom, példa a használatára:
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:
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:
K: Milyen \-szekvenciák
használhatók PHP-ben a string-literálokban? V: Egyszeres idézőjelek (avagy 'apostolok') között csak
\' és \\ használható,
jelentésük ' és \.
Kettős idézőjelek (avagy "macskakörmök") között többféle szekvencia használható
(lásd itt),
a mi szempontunkból relevánsak ezek:
\1 \12 \123 oktális kód, 1-3 számjegy, binárisan értődik, nincs konverzió
\xa \xab hexadecimális kód, 1-2 számjegy, binárisan értődik, nincs konverzió
\u{1f354} hexadecimális kód, egy vagy több számjegy,
unicode-ban értődik, utf8-ra konvertálódik,
php7-től használható
Megjegyzés: Az utóbbi eredménye mindenképpen UTF8 lesz,
nem lehet átállítani.
K: Tehát \uXXXX
szintaxist nem használhatok, csak \u{XXXX}
formát (PHP7-től kezdve)? 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, helyettesítő párok használhatók; pl:
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
htmlentities
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 Û 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:
K: A
quoted_printable_encode és
quoted_printable_decode
függvények milyen karakterkódolás esetén alkalmazhatók? V: Ezek bájtsorozatokkal dolgoznak, tehát erre nincs korlátozás,
lehet például UTF8, UTF16 vagy bármi más.
K: A
base64_encode és
base64_decode és
függvények milyen karakterkódolás esetén alkalmazhatók? V: Ezek bájtsorozatokkal dolgoznak, tehát erre nincs korlátozás,
lehet például UTF8, UTF16 vagy bármi más. K: Base64-ről szólva, Van beépített függvény
Base64url-hez is? V: Úgy látom, nincs; a Wikipedián leírt különbségek alapján házilag kell valamit tákolni, pl:
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:
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:
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
K: Az alábbi hibaüzenetet kapom a Perltől, hogyan tudom elnyomni?
perl: warning: Setting locale failed.
perl: warning: Please check that your locale settings:
LANGUAGE = (unset),
LC_ALL = (unset),
LC_CTYPE = "hu_US.CP856",
LANG = "C"
are supported and installed on your system.
perl: warning: Falling back to the standard locale ("C").
V: Legjobb lenne, ha kinyomoznád, hogy miért nem sikerült beállítani a lokalizációt,
mit kell telepíteni, vagy a locale-gen-t futtatni, stb.
Mindazonáltal van 'gyorsmegoldás' a hibaüzenet elnyomására:
export PERL_BADLANG=0
K: Mit jelent a Wide character in print hibaüzenet? V: Egy stringet akartál egy stream-be írni, és ennek során az alábbi három dolog találkozott össze:
a string utf8-as kódolású, a stream nem utf8-módú, és a karakter nem konvertálható a stream-hez rendelt
kódolásban (ennek az alapértelmezése lehet pl. iso-8859-1, ekkor ez U+FF fölötti unikódú karaktert jelent,
amilyen például a magyar őŐűŰ).
K: Hogyan lesz a stream utf8-módú, illetve hogyan lehet
a kódolást beállítani? V: Az open MODE paraméterében
megadhatod a :utf8 vagy :encoding(kód) opciót.
(Az :utf8 és :encoding(UTF-8) hatása nem egészen egyforma:
olvasás esetén az utóbbi ellenőrzi is az input utf8-validságát.)
Van még a binmode,
amivel már megnyitott fájlokat (pl STDIN, STDOUT, STDERR) tudsz átállítani;
illetve az open pragma amivel a fájlnyitásokhoz
globálisan adhatsz meg beállítást, illetve az előbb felsorolt STDxxx fájlokat is átállíthatod, pl.:
use open ':std', IO=>':encoding(UTF-8)'
K: Úgy tűnik nekem, hogy a LC_CTYPE-ot
nem veszi figyelembe, tudok tenni valamit ezügyben? V: Ilyesmit lehetne próbálni:
my $lc_ctype= (split '\.', $ENV{'LC_CTYPE'})[-1];
open my $outfile, ">:encoding($lc_ctype)";
K: És mit jelent a use utf8;? V: Azt, hogy maga a forrásprogram (vagyis a Perl script) utf8-ban értendő.
Ennek az ellenkezője a no utf8; ami azt jelenti, hogy valamilyen
8-bites kódolásban van. Ezek hatása a fájl vagy a tartalmazó blokk végéig tart.
K: Milyen kódolásban fog ez kiíródni? V: Unixon a LC_CTYPE-nak megfelelően, Windowsban a
default ANSI charset szerint. (A \x12-szekvenciát
ISO-8859-1-szerint értelmezi, a 'coding'-tól függetlenül.)
Megjegyzés: ha olyan karaktert írnánk ki, amely nem ábrázolható
a kimeneti kódolásban, vagy ha a forrásprogram kódolása nem azonos a
coding-ban megadottal, akkor hibaüzenetet vagy hibás eredményt
kapunk.
K: A chr függvény
paramétere milyen kódolás szerint értendő? V: Unicode, függetlenül 'coding'-tól, pl.:
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
(lásd a file.encodingbeállítást). 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: JNI esetén hogyan férhetek hozzá egy String tartalmához? V: Három lehetőséget látok:
A String.getBytes-t hívod neked tetsző kódolással, és a kapott bájttömbhöz a
GetArrayLength, GetByteArrayElements, ReleaseByteArrayElements függvényekkel férsz hozzá.
A GetStringLength, GetStringChars, ReleaseStringChars függvényeket használod,
ekkor UTF-16-ban kapod a karaktereket.
A GetStringUTFLength, GetStringUTFChars, ReleaseStringUTFChars függvényeket használod,
ekkor módosított UTF-8-ban kapod a karaktereket.
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 "🍔�" 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:
K: Milyen műveleteket használhatok bináris üzenetekhez? V: Itt van rögtön a write,
a getDataLength és a readFully.
(Ezek bájttö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;
K: Java programomban
SimpleDateFormat-ot
használok. Egyes értékek (nap és hónap neve, am/pm jelző) lokalizáció-függőek, ezek működése mitől függ? V: Tapasztalataim szerint Unixban a LC_CTYPE-tól, Windows-on a
-Duser.language-tól.
K: A JAXB által létrehozott XML milyen encoding-ban keletkezik? V: Alapértelmezésben UTF-8, de a javax.xml.bind.Marshaller.setProperty
metódussal másra is be lehet állítani, pl.:
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ÁÄÉÍÓÖÚÜŰ]+$/';
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ÁÄÉÍÓÖÚÜŰ]+$");
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));
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:
Komplett tömböket és struktúrákat is kezel,
az ékezetes betűket UTF-8-ban vagy \u
szekvenciákkal adhatjuk meg.
Az egyetlen problémás pont az, ha a HTML-be ágyazott JavaScript-ben
véletlenül a </script> string fordul elő,
ugyanis a böngésző azt a script lezárásnak tekinti. A defenzív megoldás
az, ha minden / jelet "escape-elve",
\/ formában adunk meg.
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/\"/\"/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.
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:
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 (Õ) 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.
K: Lehetséges, hogy az
SAP program egyedi számokat rendel a különféle kódolásokhoz? V: Természetesen, anélkül nem vesznek komolyan egy enterprise rendszert.
Külön bónusz, hogy teljes lista nem érhető el, néhány érték az internetről összevadászva:
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:
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:
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:
CharToOemCharToOemBuffOemToCharOemToCharBuff.
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:
GetACPGetOEMCPCode 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
kieg: ez egy másik Windows verzióban nem működött, ezért
nem javaslom a használatát */
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, pl.:
K: Milyen környezeti változókkal lehet befolyásolni
a setlocale működését Windows-ban? V: Ha a kísérleteim nem csalnak, akkor semmilyenekkel, tehát ha a második
paraméter a default-ot jelentű üres string, akkor csak a registry-ből tájékozódik,
nem környezeti változókból.
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. Nem teszteltem alaposan, de első kísérletre úgy tűnik,
hogy minden billentyűzetről olvasó művelet (pl fgets, getline),
egységesen nem tud beolvasni ékezetes betűt tartalmazó szöveget. K: Más baj nincs vele? V: Egyes régi Windows-ok (pl. XP) a
CHCP 65001-et 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):
Megj.: természetesen ez a file.encoding opció
nem csak Windows-ban használható.
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:
BillKódNé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: Ugyanott a Vezérlőpulton (egyes Windows10 verziókban)
van egy olyan rész is, hogy
Béta: Unicode UTF-8 használata a globális nyelvi támogatáshoz
Ez vajon mit csinál? V: Egyelőre annyit vettem észre, hogy ezt beállítva a
setlocale függvényben használható lesz
az UTF8-at jelentő 65001 érték, pl:
setlocale(LC_ALL, "hungarian_Hungary.65001");
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:
K: Windows esetén van eszköz saját billentyűkiosztás
létrehozására? V: A Microsoft Keyboard Layout Creator (MSKLC) nevű program való
erre, ingyen letölthető a Microsoft-tól; vannak gyenge pontjai, például kell neki a .NET framework
valamelyik régi verziója, valamint hogy bizonyos képernyőfelbontásoknál nem (jól) működik.
K: Ezzel tudom például a NumLock billentyűt letiltani? V: Azt pont nem, de ügyes registry-haxolással ez is megoldható, lásd pl.
itt:
K: (Windows-hoz érintőlegesen kapcsolódó kérdés)
A programom CSV-fájlt állít elő. Tudom befolyásolni, hogy Excel-ben
hogyan jelenjen meg a tartalma?
V: Valamennyire igen, de vedd figyelmebe, hogy ezek nem valamilyen szabványon
alapulnak (pl RFC4180),
tehát nem garantálható, hogy minden kontextusban működnek.
• Ha van a fájl legelején BOM, akkor az Excel a tartalmat
UTF8-ként értelmezni, egyébként a default ANSI kódolás (pl. Windows-1250) szerint.
• Az elválasztókaraktert (tipikusan vessző vagy vesszőpont) megadhatod a fájl elején
egy külön sorban, pl:
sep=;
• Az egyes mezőket dátumként vagy számként értelmező mesterséges intelligencia
semlegesíthető, ha időzőjelek közé tesszük az értéket, és egyenlőségjelet teszünk elé,
pl.:
head: OpenSSL;OpenSSH
volt: 3.1.1; 8.7 -- ebből lehet pl. egy dátum és egy szám
lett: ="3.1.1"; ="8.7" -- ez megmarad
K: Excel-ben a
KARAKTER és KÓD
(eredetileg CHAR és CODE)
függvények milyen kódolás szerint működnek? V: A kísérletek szerint Default ANSI kódolásban; a KÓD függvény
az abban nem található karakterekre egységesen a kérdőjel kódját (63) adja.
Újabban van már UNIKARAKTER
(erdetileg UNICHAR) és
UNICODE függvény is.
Példa magyar Excel-ből (figyeljünk a magyar Ő és a hullámos Õ közötti külünbségre):
K: És az Excelen belüli VBA-ban? V: A fentiekkel összehasonlítható függvények az alábbiak: asc(char) ANSI karakterkód (ha a karakter benne van a 'default ANSI'-ban,
ha nincs, az eredmény meglepő lehet, pl. Asc('Õ')=Asc('O')) ascW(char) unikód (bármilyen karakteré) char(ansikód) kód alapján karakter (default ANSI) charW(unikód) unikód alapján karakter
Valamint, ha jól vélem látni, az editor-ablakban eleve csak Default ANSI
karakterek használhatók.
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:
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.
Példa:
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: Ha a rendszeremben (pl. Centos) nincs
locale-gen, akkor mit tehetek? V: A locale-gen csak egy script, a tényleges
munkát a localedef végzi, például ebből a sorból
hu_HU.win-1250 CP1250
ez a localedef hívás lesz:
localdef -i hu_HU -c -f CP1250 -A /usr/share/locale/locale.alias hu_HU.win-1250
# -i inputfile (pl. /usr/share/i18n/locales/hu_HU)
# -c force (kisebb hibákon lépjen át)
# -f charmapfile (pl. /usr/share/i18n/charmaps/CP1252.gz)
# -A aliasfile
# a végén a teljes név
Tehát ha nincs locale-gen, akkor
a localedef közvetlen hívásával próbálkozhatsz.
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: 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:
Megj: Ez nem az előző helyett ajánlom, csak annak kiegészítéseképpen.
K: A mcedit-ben
(illetve a mcview-ben) egyes
ékezetes karakter rosszul jelennek meg,
vagy ^A szekvencia látszik helyettük,
mit tegyek ez ellen? V: Az a terminál(emulátor)od fajtájától (utf8 vagy 8-bites), és a fájl kódolásától függ.
(Mindenesetre a LC_CTYPE enviró a terminál beállításával
(pl. PuTTY esetén ez a
Translation funkció) összhangban kell legyen,
különben garantált a hibás működés.)
•
utf8-as terminálon a LC_CTYPE enviró legyen
hu_HU.UTF-8 (vagy en_US.UTF-8 stb.),
a mcedit Choose Codepage (ALT+c billentyű) beállításánál
pedig a fájl tartalmának megfelelő kódolást válaszd.
•
8-bites terminálon a LC_CTYPE enviró legyen összhangban
a terminál kódolásával (pl. hu_HU.ISO-8859-2 vagy
fr_FR.CP1252), a Choose Codepage-nél
pedig a fájl tartalma szerint vagy az UTF-8-at (c billentyű),
vagy a megfelelő 8-bites kódolást válaszd.
Természetesen 8-bites terminálon semmiképp sem jelennek meg azok a karakterek,
amelyek nincsenek benne a terminál kódkészletében
(ilyenkor pontokat vagy ^A szekvenciákat jelenít meg az editor).
Ha a fájl kódolása egyezik a termináléval (pl. mindkettő ISO-8859-2), akkor
a < No translation > beállítást is használhatod). Megj: A 4.8.20-as verzió előtt volt egy
hiba,
ami zavart okozott, amikor utf8-as fájlt szerkesztettünk 8-bites terminál(emulátor)ban.
K: Az stty -a kimenetében
láttam egy iutf8 (vagy -iutf8) részt.
Azt jelenti ez, hogy a kernel is követi, hogy UTF8-at használok-e, vagy sem? Ha igen,
miért? V: Igen, a kernel (legalábbis Linux esetén) tudja, hogy UTF8-at használsz, ennek akkor
van jelentősége, amikor 'főzött' (cooked) módban használod a billentyűzetet.
Ez egyszerűbben mondva azt jelenti, hogy az Enter megnyomásáig a felhasználói program
nem kap semmilyen inputot, akkor viszont egyszerre az egész sort.
A gépelés közben szerkesztésre használhatod (egyebek mellett) a BackSpace billentyűt,
ez törli az utolsó karaktert.
No ehhez a művelethez van szükség arra az információra, hogy egy karakter egy bájtot,
vagy egy utf8-szekvenciát jelent-e. Példa:
$ cat | od -tx1
Lő<BackSpace><Control+D;
0000000 4c 0a # helyes működés
0000000 4c c5 0a # hibás működés: utf8-at használunk, de a 'stty iutf8' nincs érvényben
K: És ha én egzotikus Unix-rendszert használok, ahhol nincs ilyen beállítás? V: Az pech.
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)
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):
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:
K: X Window alatt hogy tudok saját billentyűkiosztást léterhozni? V: Egy nagyon flexiblis, rugalmas, felhasználóbarát rendszerrel,
melynek elemei pl. az xkbcomp, setxkbmap
programok. K: Ezzel azt akarod mondani, hogy nagyon bonyolult és dokumentálatlan? V: Igen.
K: Ezzel tudom például a NumLock billentyűt letiltani? V: Például ez a script megcseréli a NumLock és a nálunk nem létező "Kata" billentyűket:
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.
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
a CESU-8-at jelenti.
K: És hogy mondják MySql nyelven azt,
amit mindenki más UTF-8-nak nevez? V: Annak pedig 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.
K: Kicsit pontosabban mit állít be a
SET NAMES? V: Alapvetően ezt a három session-specifikus rendszerváltozót:
mysql> set names utf8mb4;
mysql> show variables where variable_name in
('character_set_connection','character_set_client',
'character_set_results','collation_connection');
+--------------------------+--------------------+
| Variable_name | Value |
+--------------------------+--------------------+
| character_set_client | utf8mb4 |
| character_set_connection | utf8mb4 |
| character_set_results | utf8mb4 |
| collation_connection | utf8mb4_general_ci |
+--------------------------+--------------------+
K: Nem alapvetően számolva ez négy darab, nem pedig három. V: Hát igen, a három charset-en kívül itt van még a collation, vagyis string-összehasonlítási mód.
A 'character_set_connection' automatikusan beállítja a 'collation_connection'-t is valamilyen default értékre,
ezt így lehet felülbírálni:
mysql> set names utf8mb4 collate utf8mb4_hungarian_ci;
mysql> show variables where variable_name in
('character_set_connection','character_set_client',
'character_set_results','collation_connection');
+--------------------------+----------------------+
| Variable_name | Value |
+--------------------------+----------------------+
| character_set_client | utf8mb4 |
| character_set_connection | utf8mb4 |
| character_set_results | utf8mb4 |
| collation_connection | utf8mb4_hungarian_ci |
+--------------------------+----------------------+
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 (általános esetben) a NLS_LANG környezeti változóból
(Windows esetén esetleg registry-entryből), pl.:
(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
A valóság ennél egy kicsit bonyolultabb, van egy NLS_NCHAR
nevű változó is. Bővebb tárgyalás helyett egyelőre annyit jegyezzünk meg,
hogy új fejlesztésekhez az alábbi beállítás ajánlott:
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.
Ha Pro*C-t használunk, csak a környezeti változót használhatjuk;
OCI(Oracle Call Interface) használatánál
az OCIEnvNlsCreate függvényt is használhatjuk,
aminek paraméterként fogadja a karakterkészlet kódját (illetve két karakészlet kódját).)
PHP esetén az oci_connect
negyedik paraméterét használhatjuk erre a célra,
ami egyszerűbb és biztosabb módszer a környezeti változók használatánál.
(Megjegyzés: A PHP belsőleg a fentebb említett OCIEnvNlsCreate
függvényt hívja, de ugyanazt az értéket adja mindkét karakterkészletnek.)
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: Ha jól értem, a CONVERT
harmadik paramétere (from-charset) opcionális: mi az alapértelmezés? V: Az adatbázis karakterkódolása (NLS_CHARACTERSET),
ezt felhasználva például egy ékezettelenítő utasítás így nézhet ki PL/SQL-ben:
v_ektelen := convert (v_ekezetes, 'US7ASCII');
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
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 UTFEEBCDIC-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: Ebben pont nem látom a karakterkészletet. V: Azt még én sem találtam, sőt az Oracle leírásai sem mondanak semmi bíztatót.
Itt van például a sys_context('USERENV','LANGUAGE'):
amit visszaad,
az úgy néz ki, mint a NLS_LANG tartalma
(language_territory.charset), de benne a harmadik rész nem
a kliensoldali karakterkódolás (hanem a szerveroldali).
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: A közönséges literálokban nem, de van egy külön
U' szintaxis, amivel igen
(a literál NCHAR tipusú lesz), pl.:
SQL> select u'\d83c\df54\20ac' from dual;
🍔€
SQL> select dump(u'\d83c\df54\20ac',1016) from dual;
Typ=96 Len=6 CharacterSet=AL16UTF16: d8,3c,df,54,20,ac
Van továbbá egy
unistr
függvény is erre. K: És mi ennek az unistr 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: Az Oracle melyik függvényével tudok unikód és karakter-típus között konvertálni? V: Úgy tűnik, NCHAR/NVARCHAR típusok
estén erre is van lehetőség (legalábbis ha a
NLS_NCHAR_CHARACTERSET paraméter értéke AL16UTF16)
declare
nc nchar;
n pls_integer;
begin
nc:= nchr(369); -- várhatóan ű
n := ascii(N'Ž'); -- várhatóan 381
end;
Megjegyzés: vegyük észre, hogy ASCII függvénynek nincs
NASCII párja, hanem a paraméter típusától függ a működése, pl.:
K: Hogyan tudom ezt char és nchar
közötti konverzióval kombinálni? V: Például így:
declare
c char;
n pls_integer;
begin
c:= to_char(nchr(369)); -- várhatóan ű
n:= ascii(to_nchar('Ž')); -- várhatóan 381
end;
Megjegyzés: ha például egy CARON (U+02C7) karaktert akarunk ábrázolni,
de úgy, hogy se a kliens, se a szerver karakter-beállítása ne számítson,
azt így tehetjük meg:
declare
caron char := to_char(nchr(711));
begin
dbms_output.put_line(caron);
dbms_output.put_line(ascii(caron));
dbms_output.put_line(ascii(to_nchar(caron)));
end;
K: Ebben az utolsó példában az tűnt fel, hogy a második sor kimenete a
szerver NLS_CHARACTERSET beállításától függően
161, 183 vagy 52103 is lehet. V: Ez sajnos igaz, emiatt talán nem is érdemes így használni az
ascii függvényt. Az 52103 például a kétbájtos UTF8-szekvencia
(cb 87) számmá alakításából származik.
A harmadik sorban szereplő változat fixen az unikódot adja meg.
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:
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:OCI esetén mi az
OCI_ATTR_CHARSET_FORM és
OCI_ATTR_CHARSET_ID jelentősége? V: Az előbbi azt mondja meg, hogy milyen kódolás menjen a dróton,
az utóbbi azt, hogy milyen kód van/legyen a kliensoldali változóban. Táblázatosan:
K: Csak az ismétlés kedvéért: ha elveszítem a görög vagy cirill
betűimet, akkor melyik oldal a hibás, a kliens, vagy a szerver? V: Mindkettő lehetséges, pontosabban mondva, csak akkor használhatsz minden unikódot,
ha mindkét oldal unikód-képes (tipikusan AL32UTF8 vagy AL16UTF16).
Például az alábbi kombináció biztonságosnak tűnik:
szerveren:
NLS_CHARACTERSET=(valami)
NLS_NCHAR_CHARACTERSET=AL16UTF16
mezők típusa: NCHAR, NVARCHAR2, NCLOB, ...
kliensen:
NLS_LANG=(valami)
NLS_NCHAR=AL32UTF8
mezők típusa:
VARCHAR CHARACTER SET IS NCHAR_CS (Pro*C)
OCI_ATTR_CHARSET_FORM=SQLCS_NCHAR (OCI)
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.:
Kieg: Érdekességként megemlítem, hogy a 10.2.0.1 verziójú Oracle kliens nem boldogul
a -Duser.country=hu -Duser.language=en kombinációval,
de a 10.2.0.5 verzióban ez is működik.
K: Az Oracle adatbázisban mezőnként vagy táblánként tudom
állítani a karakterkódolást (vö MySQL) ? V: Adatbázisonként, mivel ezt a CREATE DATABASE parancs
CHARACTER SET (és NATIONAL CHARACTER SET)
záradékával tudod beállítani, és később megváltoztatni nem lehet.
K: Ha változó hosszú karakterkódolást
(értsd: AL32UTF8-at)
állítok be, az járhat sebesség-csökkenéssel? És lehet gondom abból, hogy az ékezetes szöveg
nem fér el abban a mezőben, amiben az ékezet nélküli elfér? V: Igen. (FixMe: ide kellene a NLS_LENGTH_SEMANTICS leírása.
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.
A PDF bináris fájlformátum, amiről egyelőre nincs itt sok adat,
de az megnyugtató, hogy az ékezetes betűk itt is lehetnek problémásak.
Adott esetben az is előfordulhat, hogy az Acrobat Reader-ben jól látszanak
az őŐűŰ betűk, de a vágólapon át kimásolva már oOuU látszik.
Unixban a PDF-fájlokban használt betűtipusokat a pdffonts(1)
programmal listázhatjuk, pl:
$ pdffonts test1.pdf # ebben nyugat-európai õÕûÛ betűk vannak
name type encoding emb sub uni object ID
------------------------------------ ----------------- ---------------- --- --- --- ---------
Courier-Bold Type 1 WinAnsi no no yes 6 0
$ pdffonts test2.pdf # ebben magyar őŐűŰ betűk vannak
name type encoding emb sub uni object ID
------------------------------------ ----------------- ---------------- --- --- --- ---------
CourierNewPS-BoldMT TrueType Custom no no yes 7 0
$ pdffonts test3.pdf # ebben magyar betűk vannak, de a copy+paste során 'elromlanak'
name type encoding emb sub uni object ID
------------------------------------ ----------------- ---------------- --- --- --- ---------
TimesNewRomanPS-BoldMT TrueType Custom no no no 9 0
K: Mifene az az FPDF? V: Egy PHP modul, PDF-ek előállítására szolgál.
A továbbiakhoz az 1.8.6-os (vagy újabb) verziót használjuk. K: Hogyan és hová kell telepíteni? V: Erről nincs sok szó az install.txt-ben sem, Unixon valami ilyesmit
lehetne javasolni:
mkdir -p /usr/local/share/php
cd /usr/local/share/php
tar xzf /download/fpdf186.tgz # unzip /download/fpdf186.zip
ln -s fpdf186 fpdf
chmod -R o-w fpdf/ # az alapbeallitas nem egeszen biztonsagos ('world writable')
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: 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:
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/
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).
K: És a
TCPDF
is valami hasonló? V: Igen, van hozzá
leíráspé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');
?>
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:
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:
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.
K: Valamelyik levelezőprogram
(most nem akarom az androidos Outlookot nevesíteni)
minden ékezetes betű helyett egy ilyen szekvenciát jelenít meg:
ďż˝
Mi lehet ennek az oka? V: Az email
ISO-8859-2-ben
van, de a program egyik agyféltekéje
UTF8-ban gondolkodik, ezért az ékezetes betűket 'hibás UTF8-szekvencia'-ként
azonosítja, és lecseréli őket � -re
(REPLACEMENT CHARACTER, U+FFFD, UTF8-ban EFBFBD)
A program másik agyféltekéje tudja, hogy ISO-8859-2-ben
kellene működni, ezért az EF,BF,BD bájtokat eszerint is jeleníti meg:
U+10f,U+17c,U+2dd azaz
ďż˝). K: És ha olyan szekvenciát látok, hogy
�, mire gondoljak? V: Ez az előbbi esettel analóg, csak
ISO-8859-1 kódolással:
U+ef,U+bf,U+bd azaz
� K: És mi lenne a megoldás? V: Nincs megoldás, a levél küldőjének kellene UTF8-ra áttérnie.
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ódkarakterunikó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).
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 az é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.
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)
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.keyopenssl 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:
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 bájt (base64 nélkül 24 bájt):
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ó.
K: Ha közben fejlődtek a verziók, akkor a fentiket parancsokat lehet "korszerűbben"
is használni? V: Például:
K: Egy on-topic kérdés: az OpenSSL-t certificate-request (CSR)
előállítására akarom használni
(openssl -req),
de nem nagyon látszik figyelembe venni a LC_CTYPE-ot,
fixen ISO-8859-1-szerint értelmezi az inputot. V: Ezt én is így látom; egy további lehetőség találtam: UTF8-at használni, és megadni az
-utf8 opciót.
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:
K: A docker program paraméterezésénél
illetve a Dockerfile-ban
milyen karakterkódolást használhatok? V: Nekem nem sikerült UTF-8-tól különböző kódolást használni,
minden ékezetes betű helyett EF-BF-BD szekvencia lett,
ami az U+FFFD kódú replacement character-t jelenti.
K: A konténerben futó programmal is lehet gond? V: A LC_CTYPE-ot (és a többi érintett környezeti változót),
a megszokott módon kell beállítani, de gondot okozhat, ha a "konténeres rendszerünkben"
nincs/hiányos a locale. Debian-alapú rendszerekhez
megoldási lehetőség a Dockerfile-ban:
RUN sed -i.bak 's/override_install_langs=.*$/override_install_langs=en_US,hu_HU/' /etc/yum.conf && \
yum -y -q reinstall glibc-common
K: A docker build futása alatt
jellegzetes Perl hibaüzeneteket kapok lokalizációügyben. V: Azt javaslom, hogy a LC_CTYPE beállítását (pl.: ENV LC_CTYPE hu_HU.UTF-8)
halaszd a lokalizáció telepítése utánra.
K: Ha már úgyis a telepítek a virtuális gépbe,
érdemes a tzdata komponenst is telepíteni? V: Hát valószínűleg nem árt. Ilyesmi lenne:
Debian: RUN apt-get install -y tzdata
RedHat: RUN yum install tzdata
Alpine: RUN apk --no-cache add tzdata
K: Minden ékezets magyar betű helyett két
'furcsa karaktert' látok, mi lehet a baj? V: Az adatod UTF8-ban van, de valamelyik program azt hiszi,
hogy egybájtos kódolásban. Kieg: Egy másik lehetőség, hogy UTF8-ban kódolt adatot tévedésből még egyszer UTF8-ra konvertáltál,
így pl. az ű betűt jelentő
C5B1 szekvenciából
például C4B9C485 szekvencia lehetett,
ami Ĺą-ként
jelenhet meg. Kieg: MySql esetén a SET NAMES maradt ki.
K: Minden ékezetes magyar betű egyformán kérdőjel
(vagy valamilyen speciális jel) látszik, mi lehet a baj? V: Az adatod egybájtos kódolásban van, de valamelyik program azt hiszi,
hogy UTF8-ban (viszont annak persze hibás). Kieg: MySql esetén a SET NAMES maradt ki.
K: Az ékezetes magyar betűk jól jelennek meg,
kivéve az őŐűŰ betűket, amelyek helyett oOuU vagy õÕûÛ
esetleg kérdőjelek látszanak. V: Valamilyen beállítás iso-8859-2 kellene legyen,
de ehelyett iso-8859-1 van. Hasonló párok (mindegyikben
az első a jó):
latin2 és latin1,
windows-1250 és windows-1252,
cp852 és cp850,
ee8iso8859p2 és we8iso8859p1,
ce8bs2000 és we8bs2000.
K: Esetleg egyéb magyarázata is lehet az előbbinek? V: Igen, az hogy minden jól van beállítva, csak a használt font nem tartalmazza
ezeket a betűket. Egyes programok ilyenkor valamilyen helyettesítő fontot használhatnak
az említett a betűkhöz, így a betűk helyesen, de a környezetüktől eltérően jelennek meg,
'kirínak' a szövegből.
(Persze ez más ritkább betűkkel is előfordulhat, például az eszperantó nyelv ékezetes betűivel:
ĉĈĝĜĥĤĵĴŝŜŭŬ.)
K: PHP-ban olyan hibaüzenetet kapok, hogy
Cannot modify header information - headers already sent by
output started at… in… on line…
akkor mi a gond? V: A
http_response_code,
header,
session_start,
setcookie és hasonló
függvények a HTTP-fejrészbe tartozó információkat (magyar szóval: metainformációkat) tartalmaznak,
amelyeket a "közönséges" (tehát a HTTP-törzsrészbe tartozó) információk előtt kell megadni.
A hibaüzenetben benne van az is, hogy hol lépett fel a hiba, és az is, hogy hol volt már
korábban kimenet. Ahogy fentebb is említettük, egy szóköz vagy BOM
is kimenetnek számít, úgyszintén az is, ha egy függvény esetleg hibaüzenetet ír ki.
K: Az ékezetes betűk helyén furcsa szekvenciákat látok,
esetleg ezekből lehet következtetni valamire? V: Ez is lehetséges, nézzünk egy példát, amelyben tévedésből utf8-ra konvertálunk valamit,
ami már amúgy is utf8 volt:
Megjegyzés: az U+91 (PU1, UTF8-ban c291, HTML-ben ‘)
egy nem nyomtatható vezérlőkaraker, de attól még a böngésző símán megjelenítheti
helyette az
U+2018 (LEFT SINGLE QUOTATION, ‘ UTF8-ban e28098, HTML-ben ‘)
karaktert, hogy windows1252-kompatibilis legyen. Egy másik példa: Egy bizonyos programban az ékezetes betűk helyett
뿯½
(U+BFEF, U+00BD) szekvencia jelenik meg.
A valószínű magyarázat az, hogy az előállító program Unicode Replacement Character-t
(� U+0FFD,
UTF8-ban EF-BF-BD) akart írni,
de valamilyen furcsa hiba miatt az UTF-8 szekvenciát (négy bájtra kiegészítve
egy bináris nullával) értelmezte két UTF-16LE karakternek:
EF-BF-BD-00 => BFEF, 00BD
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:
statikus fájl (HTML, TXT stb)
PHP (echo, print, printf stb)
Adatbázis
JavaScript
egyéb
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 ...
MSSQL: SELECT CAST (mezonev AS VARBINARY) 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.:
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 ő,
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.
Az egyes mezőkben látható három karakter jelentése: billentyű önmagában, Shift-tel, AltGr-vel.
A 'z' és 'y' gyakran fordítva, vagyis az angol kiosztás szerint helyezkedik el.