Ékezetes FAQ webművészek számára (Nem mennek az ékezetek a
szájtomon!)
K: Nem mennek az ékezetek a szájtomon, pedig már összevissza
kapkodtam,
mindent átállítottam és vissza, mit csináljak még?! V: Végső esetben próbálj meg tájékozódni, valamilyen kódkészletet
kiválasztani,
és minden beállításnál azt használni.
K: Mi az a 'minden beállítás'? V: Először jöjjön a HTTP-header, azon belül is a Content-Type,
pédául ennek az oldalnak az esetében:
Content-Type: text/html; charset=iso-8859-2
K: Na ez honnan jön? V: Statikus fájlok (HTML, CSS, JS stb) esetén a .htaccess-ben
az
AddDefaultCharset-ből
(ha az nincs, akkor az apache globális
beállítása érvényesül), dinamikus fájlok esetén ezt felülbírálhatjuk
úgy,
ha magunk állítjuk elő ezt a fejrész sort. Példa PHP-ban:
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: 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: 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 egyedik jelölés
M-^A .. M-^Z 80..9A 0x40-et hozzáadunk a karakterkódhoz
M-^[ .. M-^_ 9B..9F 0x40-et hozzáadunk a karakterkódhoz
M-normál ASCII karakter A0..FE 0x80-at hozzáadunk a karakterkódhoz
M-^? FF egyedik jelölés
K: Ha nekem jó lenne az
ISO-8859-2,
de euró-jelet (€) is szeretnék, akkor használhatom az
ISO-8859-16-ot? V: Használhatod, de az űŰ nem ugyanott van a kettőben, vagyis kovertálni kell...
akkor már jobban jársz a windows-1250-nel (abban a magyar betűk ugyanott vannak),
vagy az utf-8-cal (univerzális, korszerű megoldás).
K: Tulajdonképpen mit szabványosít az
Unicode? V: Karakterekhez rendel számokat. Azt nem írja elő, hogy ezeket a számokat
hogyan kell memóriában vagy fájlban tárolni. K: De az legalább egy remek dolog, hogy ezek a számok sosem változnak! V: Tulajdonképpen egyes tibeti és koreai karakterekkel azért történt ilyesmi, pl.:
1.0 1.1 2.0
ཀ 1000 – F40
가 3400 3400 AC00
K: Nem az lenne a legjobb, ha egyszerűen
Unicode-ot használnánk?
Ugyebár minden karakterhez hozzárendeltek egy 0 és 65535 közötti
(tehát két bájton tárolható) számot... V: Az a szám már messze túl van a 65535-ön,
például az 🍔=🍔=🍔 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: Hogyan is jön ez ki? V: Ezt a dolgot helyettesítő pár-nak hívják (surrogate pair);
a pár két fele 1024 érték közül tárol egyet-egyet
(az első a d800-dbff tartományból, a második a dc00-dfff tartományból vesz fel értéket),
így összesen 1048576 kódot lehet így tárolni.
Valahogy így tudnám ezt táblázattal szemlélteni (minden kód hexadecimális,
az unikód elé U+ -t írok, hogy gyakoroljuk ezt a jelölést,
továbbá az feltüntetem az UTF8-as és a CESU8-beli kódot is):
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)
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:
K: Na jó, de én nem is így akarom a programban
rögzíteni a stringeket, hanem L'ä' és L"öőüű" formában.
Ebben van valamilyen problémás rész vagy hibalehetőség? V: Annyi, hogy a compiler-nek tudnia kell, hogy milyen kódolásban van
a forrásprogram. Hogy ez hogyan történik, az platformfüggő lehet;
ha nem adod meg, akkor valamiféle alapértelmezés fog érvényesülni.
Például gcc esetén az -finput-charset való erre, pl (linux).:
# a source latin2-ben van
$ gcc -finput-charset=iso-8859-2 -o wchartest_l wchartest_l.c
$ ./wchartest_l
f6000000-51010000-fc000000-71010000-00000000
# a source utf8-ban van
$ gcc -finput-charset=utf-8 -o wchartest_u wchartest_u.c
f6000000-51010000-fc000000-71010000-00000000
K: Ha eltévesztem, akkor fordítási hibát kapok, vagy a futás lesz rossz? V: Igen. (A fordító észreveheti, hogy a latin2 (általában) nem valid utf8,
de a fordítva ez nem igaz.)
K: MS Windows esetén micsoda a wchar_t? V: Intel platformon 16-bites little endian, a 16-biten el nem férő
karaktereket két egymást követő wchar_t tárolja (lásd: UTF-16LE, surrogate pairs).
K: És igaz, hogy majdnem a teljes Windows API-t
'megduplázták', minden hagyományos stringeket használó függvénynek lett egy
párja, ami UTF-16-os ("széles" avagy "wide") stringeket használ? V: Igaz (pl. a CreateWindow makró
a CreateWindowA és CreateWindowW függvények valamelyikévé fog kifejtődni),
de lehetséges olyan programot írni, ami egy #define-tól (UNICODE)
függően lesz "keskeny-" vagy "széles-" karakteres.
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 multibyte-ról/-ra tudnak konvertálni
(lásd itt: Code Page Identifiers,
valamint használhatsz default ansi kód és default oem kód értékeket is (CP_ACP és CP_OEMCP),
ha platformfüggő működést akarsz elérni).
K: C-programban milyen oktális/hexa szekvenciákat használhatok
karakter- és stringliterálokban? V: Az egyre újabb szabványok egyre újabb lehetőségeket hoztak:
\000 -- egy-három oktális számjegy a 000-377 tartományból
\x00 -- egy vagy több hexadecimális számjegy (a hossz nincs korlátozva)
\u0000 -- négy hexadecimális számjegy (unikód)
\U00000000 -- nyolc hexadecimális számjegy (unikód)
Megjegyzések: Az persze lehet, hogy nem minden lehetőség használható minden platformon,
illetve hogy valamilyen compiler-opció kell hozzá (pl. gcc esetén a -std=c99).
A \x szekvenciánál a hossz nincs korlátozva,
de ha a megadott érték nem fér el a használt
karaktertípusban (char, wchar_t, char16_t, char32_t), akkor az eredmény platformfüggő
(pl hibaüzenet, az érték csonkítása, stb).
A \u és \U szekvenciáknál
az érték az adott karaktertípusnak megfelelően konvertálódik
UTF16-re, UTF32-re vagy UTF8-ra;
\x szekvenciáknál nincs konverzió.
K: Mit kell tudnom a C11-es szabvány szerinti új
char16_t és
char32_t típusokról? V: Ezek az uchar.h-ban definiált előjel nélküli integer típusok,
amelyek 16- illetve 32-bitesek. Legalább. K:Legalább?! Ugye jobb lesz nekem, ha nem gondolok bele abba, hogy mit jelent
ez a legalább? V: Határozottan. Nézzük meg inkább, hogyan tudsz literálokat definiálni ezekhez a típusokhoz:
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: 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: 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:
Megj: Nem kell mindig kiírni a kódkészletet (vagyis az UTF-8-at),
ha használod az
mb_internal_encoding
függvényt.
K: Milyen bajt okozhat, ha pl. a
hagyományos substr
függvényt használom? V: Mondjuk az, hogy ügyesen kettévágsz egy utf8-szekvenciát...
K: Mit tegyek, ha az
fgetcsv
függvény hibásan működik, ha ékezetes betűk vannak a mezők elején? V: Előzőleg egy
setlocale
(LC_CTYPE, "...") hívás segíthet.
(Szerk. megj.: A problémát nem tudtam reprodukálni, a PHP verziójától is függhet.)
K: Igaz, hogy a
htmlentites
függvény is okozhat gondot? V: Okozhat, mivel ez a függvény mindent &-szekvenciává alakít,
amit egyáltalán át lehet alakítani; ez magában foglalja a magyar ékezetes betűket is,
viszot a latin2-t nem támogatja (még ha meg is adjuk a harmadik paraméterben),
vagyis az Ű-ből például Û 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: 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
Az első a valódi fájl, a másik kettő szimlink.
K: Azt értem, hogy PHP-ben használhatok \xXX szekvenciákat.
Használhatok \uXXXX szekvenciákat is, mint C-ben vagy Java-ban? V: Nem, de van kerülőút: használhatod erre a célra a
json_decode
függvényt, ennek az eredménye mindig UTF-8 lesz; pl:
K: Hogy tudok Javában String és byte-vector között konvertálni? V: A String
egyik konstruktora
illetve a
getBytes
metódus szolgál erre. K: Ha jól látom, vannak olyan változatok is, amelyeknek nem kell charsetName paraméter.
Azok mire jók? V: Hogy kibabrálhassál magaddal, illetve hogy platformfüggő legyen a működés. K: Mindesetre, ha oda-vissza elvégzem a konverziót, akkor az eredetit kapom vissza, ugye? V: Kivéve, ha nem. Pl. egybájtos kódolás esetén az "ôõő"-ből vagy "ôõ?"
lesz, vagy "ô?ő". Vagy akár "???", szerencse kérdése.
K: És az igaz, hogy a 16-bites
char típusban
minden unikód elfér? V: Hát tulajdonképpen nem, de az int típusban igen,
illetve char-tömbben, vagy String-ben, UTF16 kódolással. K: Vagyis szükség esetén 'helyettesítő párok' (magyarul 'surrogate pairs') használatával? V: Igen, például a kedvenc hamburgerünket így tárolhatjuk String-ként:
"\ud83c\udf54"
K: 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 byte-tö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 byte-tömbökkel dolgoznak, tehát semmiféle konverzió nem történik.)
K: Milyen műveleteket használhatok karakteres üzenetekhez? V: Íráshoz van egy writeString művelet;
olvasáshoz a readStringOfByteLength metódust ajánlom,
a getDataLength metódussal kombinálva
(ezek gondoskodnak a karakterkonverzióról (lásd fentebb a characterSet mezőt).
Ha esetleg olyan ősrégi kliensed van, amelyben még csak sima
readString művelet van, akkor a multibyte-os kódolásokhoz
valamilyen helyettesítő megoldást kell tákolni, pl.:
message.seek(0); /* álljon az üzenet elejére */
int blen= message.getDataLength ();
String s;
if (message.characterSet==1208) { /* csak egy multibyte-os kódolást kezelünk, az UTF8-at */
byte [] binmsg = new byte [blen];
message.readFully (binmsg);
s = new String (binmsg, "UTF-8");
} else {
s = message.readString (blen);
}
return s;
K: Mi is az Sql-injection lényege? V: Legvilágosabban ezen az ábrán mutatják be:
http://xkcd.com/327/
K: Nem értem, miért okozna gondot ez a név? V: Akkor nézzük meg, hogyan működött a program:
Ezt akarta a programozó:
INSERT INTO students (name) VALUES ('<insertnamehere>');
De ez lett belőle:
INSERT INTO students (name) VALUES ('Robert'); DROP TABLE students; -- ');
K: Azannya! De biztos az,
hogy ilyen támadás minden adatbáziskezelőben és minden kontextusban lehetséges? V: Természetesen nem: előfordulhat, hogy a te esetedben csupán hibaüzenetet,
hibás működést lehet így előidézni, nem pedig teljes adattörlést.
Akarsz kockáztatni?
K: Jó, persze, nem, de azért azt megkérdezem,
hogy miért csak SQL-injection van, miért nincs például HTML-injection, PHP-injection,
shell-script-injection, és a többi? V: Megnyugtatlak: ezek mind előfordulhatnak, és mind ellen védekezni kell.
Alapvetően két gond van:
1. A hibás (vagy rossz-szándékú) adat olyan karaktereket tartalmaz,
amiknek nem szabadna ott lenniük.
2. Az adat teljesen legálisan tartalmaz olyan karaktereket,
amik megzavarhatják pl. az SQL-értelmezőt.
K: Akkor már értem, mi a baj. És mi lenne a megoldás? V: Nos, ha például előre tudjuk, hogy mik a megengedhető karakterek,
akkor ellenőriznünk kell az input-ot. Egy példa PHP-hez:
/* csak magyar kis- és nagybetűk, valamint számjegyek (és nem üres!) */
function Test ($s)
{
$pattern= '/^[0-9a-záäéíóöőúüűA-ZÁÄÉÍÓÖÚÜŰ0-9]+$/';
printf ("check(%s)=%d\n", $s, preg_match ($pattern, $s));
}
Egy pedig Javához:
/* csak magyar kis- és nagybetűk, valamint számjegyek (és nem üres!) */
static void Test1 (String s) {
boolean fOk= s.matches ("^[0-9a-záäéíóöőúüűA-ZÁÄÉÍÓÖÚÜŰ0-9]+$");
System.out.println ("Check ('" + s + "'): " + fOk);
}
K: És ha az input legálisan is tartalmazhat
mindenféle jeleket? V: Az egyik legjobb lehetőség az adatok és a kódok elválasztása;
SQL-esetében a bind változók valók erre
(lásd a következő fejezetben).
Ha erre nincs lehetőség, a helyzettől függő módszereket kell használni ahhoz,
hogy 'veszélyes' karaktereket tartalmazó adatot is jól kezeljünk.
1. példa:Oracle
esetében a string-literál belsejében lévő aposztrófákat meg kell duplázni:
select 'Ezt mondta: ''hello''' AS idezet from dual;
IDEZET
-------------------
Ezt mondta: 'hello'
Ha pl. Javában alkotunk, ilyesmit írhatunk:
String ora_sql = "UPDATE tabla SET mezo='"+param.replaceAll("'", "''")+"'";
PHP-s példa:
$ora_sql = sprintf ("UPDATE tabla SET mezo='%s' WHERE ...",
str_replace ("'", "''", $param));
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:
5. példa: PHP és JavaScript között akarunk adatot cserélni.
Erre való a JSON tehcnológia
(lásd még:
wiki:JSON,
PHP:JSON,
w3schools:JSON)
ami komplett tömböket és struktúrákat is szépen kezel.
Viszont, ha méréseim nem csalnak, csak UTF-8-at szeret. (Újabb érv az átállás mellett!).
6. példa: CSV fájlt akarunk előállítani/elemezni. Ugyebár ennél semmi sem egyszerűbb, de azért gondoljunk a következőkre:
(1) ha egy mezőben elválasztójel (magyar beállítás esetén vesszőspont), sorvége, vagy idézőjel (rendszerint "macskaköröm") van,
akkor a mezőt kötelező idézőjelek közé tenni, egyébként csak szabad idézőjelek közé tenni.
(2) ha egy mező idézőjelek között van, akkor a benne előforduló idézőjeleket meg kell duplázni.
7. példa: shell-ben írt CGI-program fájlneveken megy végig,
és <img src="fájlnév"> tagokat állít elő.
Nincs semmi gond, amíg egy haxor létre nem hoz egy
abc"><h1>Rossz az oldalad.jpg
nevű fájlt (ami teljesen legális fájlnév Unixban!), és ezzel elcsúfítja a kimenetet.
Megoldási lehetőség a shell scriptben:
for i in *.jpg; do
i2="$(printf '%s' "$i" | sed 's/\"/\"/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: 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 */
setlocale (LC_CTYPE, "hun"); /* alapértelmezett magyar kódolás, valószínűleg windows-1250 */
Megj: Ezt persze a fenti CHCP-vel (és a programíráskor használt kódkészlettel)
összhangban érdemes beállítani;
ne felejtsük el, hogy a LC_ALL magában foglalja a
LC_CTYPE-ot is.
K: UTF8-at is lehet használni a DOS-ablakban? V: Igen, a beállítás: system ("CHCP 65001"); K: Ez így biztos jó lesz? V: Nem biztos. Régi Windows-ok (pl. XP) ezt azzal nyugtázzák,
hogy az illető CMD.EXE példány képtelenné válik batch-ek (.BAT, .CMD) futtatására.
(Ha indítunk egy batch-et, hibaüzenet nem jön, de a batch sem hajtódik végre.)
Például a TortoiseCVS CVS.EXE programja csinál ilyet.
K: A Command Prompt-ban futtatott java ékezetei sem egészen jók...
vagy egy magyar betű sem jó, vagy csak az ő/ű nem jó. V: Próbálkozz meg az alábbiak valamelyikével (az első kettő nagyjából ugyanaz, akkor használhatóak,
ha volt CHCP 1250 előtte, a harmadik akkor,
ha az alapértelmezett CHCP 852 van érvényben,
az utolsó pedig az UTF8 (CHCP 65001) esetére):
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: Ennek van köze az alábbi registry-entrykhez?
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: 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.
K: A linux virtuális termináljain (Alt+F1...Fn)
mi van,
UTF-8, vagy 8-bites kódolás (pl ISO-8859-2), vagy szabályozható? V: Szabályozható, programból az
ioctl (KDSKBMODE, K_XLATE/K_UNICODE)
rendszerhívással (az első a 8-bites);
shell-ből a
kbd_mode -a/-u vagy az
unicode_stop/unicode_start paranccsal;
rendszerinduláskor a
vt.default_utf8=0/1 kernelparaméterekkel
(lásd a lilo/grub append parancsát).
K: És, gondolom,
a LC_CTYPE változót is ezzel
összhangban kell beállítanom... Ugye azt teljesen szabadon állíthatom be? V: Teljesen szabadon a localedef --list-archive
által felsoroltak közül érdemes választanod;
ha az nem elég, bővitsd a /etc/locale.gen listát,
és futtasd a locale-gen-t
(mindez disztribúciófüggő lehet, olvasd el a locale-gen manuálját).
K: De ha én mondjuk
IBM852-es
vagy Windows-1250-es
kódolást szeretnék használni Linuxon, akkor megsemmisül a világegyetem, vagy ez is lehetséges? V: Méréseim szerint ez is lehetséges, ha vannak ilyen fájlok a
/usr/share/i18n/charmaps könyvtárban
(ez persze disztribúciófüggő lehet), pl.:
$ cat /etc/locale.conf
...
hu_HU ISO-8859-2
hu_HU.IBM852 IBM852
hu_HU.CP1250 CP1250
hu_HU.UTF-8 UTF-8
...
# locale-gen
Generating locales (this might take a while)...
...
hu_HU.ISO-8859-2... done
hu_HU.IBM852... done
hu_HU.CP1250... done
hu_HU.UTF-8... done
...
Generation complete.
$ LC_ALL=hu_HU.IBM852 mc # nézzük meg a HELP-et (F1)
K: És az emulált terminálokon (xterm és vidéke)? V: Az emulátor az indulásakor a LC_CTYPE alapján dönti el,
hogy egybájtos vagy unikódos módban működjön-e. Valahogy így lehet kipróbálni:
LC_CTYPE=hu_HU.ISO-8859-2 xterm -e sh -c 'showkey -a' &
LC_CTYPE=hu_HU.UTF-8 xterm -e sh -c 'showkey -a' &
A magyar ékezetes betűk billentyűit próbájuk ki, látni fogjuk a különbséget.
K: 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: 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: 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' azt jelenti,
hogy a 0 és 65535 (0ffffH) közötti unikódok egyenként 1-3 bájton tárolva,
míg a világ többi részén az UTF-8 azt jelenti,
hogy a 0 és 2097151 (1fffffH) közötti kódok 1-4 bájton tárolva.
K: És hogy mondják MySql nyelven azt, amit mindenki más UTF-8-nak nevez? V:: utf8mb4 a neve.
K: Pótkérdés: és az biztos, hogy az UTF-8-ban legfeljebb négybájtosak a kódok? V: A korábbi szabvány
(RFC2779)
hatbájtos kódokat is megengedett,
a későbbi
(RFC3629)
ezt négy bájtra csökkentette.
K: Oracle-ről valamit? V: Oracle-adatbázis esetén az Oracle kliensoldali programja végzi a koverziót
a szerver és a felhasználó kódkészelete között,
az előbbit értelemszerűen a szervertől tudja meg,
az utóbbit a NLS_LANG környezeti változóból
(Windows esetén esetleg registry-entryből), pl.:
(Az első rész az üzenetek nyelvét, a második a területi beállításokat
adja meg.)
Nem kell minden részt megadnunk, például csak a karakterkészlet beállítása:
export NLS_LANG=.EE8MSWIN1250
Megj.: Ezt a beállítást még az Oracle-connect előtt érdemes
elvégezni, annál is inkább, mert (más beállításoktól eltérően)
menet közben nem lehet módosítani ALTER SESSION paranccsal. Kiegészítés: PHP esetén az oci_connect
negyedik paraméterét is használhatjuk erre a célra. Megj.: Ne felejtsük el, hogy ha 64-bites Windows-ban
32-bites Oracle-t használunk akkor a registry-ben
nem a HKLM\SOFTWARE\ORACLE,
hanem a HKLM\SOFTWARE\Wow6432Node\ORACLE a barátunk.
(De még inkább a környezeti változó használata registry helyett.) Érdekesség: A NLS_LANG-ot nem lehet AL16UTF16-ra állítani
(illetve lehet, de nem fog működni).
A magyarázat az, hogy ez a NLS_NCHAR szokásos/alapértelmezett
értéke. Ez kb annyira logikus, mint hogy az ég színe nem lehet kék,
hiszen a tenger színe a kék.
K: Jól tudom, hogy az Oracle-nek van egy saját
CONVERT
függvénye? V: Igaz, használata:
CONVERT (<mit>, <mibe>, <miből>)
Például, ha az Sql*Plus-t használod, és ISO-8859-2 a karakterkészleted
(Oracle nyelven: EE8ISO8859P2), akkor így próbálhatod ki:
SQL> select dump (convert ('árvíztűrő', 'UTF8', 'EE8ISO8859P2'), 16)
as hexutf8 from dual;
HEXUTF8
----------------------------------------------------
Typ=1 Len=31: c3,a1,72,76,c3,ad,7a,74,c5,b1,72,c5,91
K: Jól látom, hogy az Oracle saját neveket használ
a kódkészletekre? V: Igen, továbbá egy egyedi számot is hozzárendel.
Az alábbi konverziós függvények lehetnek érdekesek:
NLS_CHARSET_ID,
NLS_CHARSET_NAME,
UTL_I18N.
MAP_CHARSET.
Példa:
select nls_charset_name(2000),
nls_charset_id('ce8bs2000'),
utl_i18n.map_charset('ee8iso8859p2',0,0)
from dual;
AL16UTF16 233 ISO-8859-2
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: A szerver beállításait hogyan lehet lekérdezni? V:A NLS_DATABASE_PARAMETERS a barátunk:
SQL> select * from nls_database_parameters;
PARAMETER VALUE
------------------------------ ----------------------------------------
NLS_NCHAR_CHARACTERSET AL16UTF16
NLS_LANGUAGE HUNGARIAN
NLS_TERRITORY AMERICA
NLS_CURRENCY $
NLS_ISO_CURRENCY AMERICA
NLS_NUMERIC_CHARACTERS .,
NLS_CHARACTERSET EE8ISO8859P2
NLS_CALENDAR GREGORIAN
NLS_DATE_FORMAT DD-MON-RR
NLS_DATE_LANGUAGE HUNGARIAN
NLS_SORT HUNGARIAN
NLS_TIME_FORMAT HH.MI.SSXFF AM
NLS_TIMESTAMP_FORMAT DD-MON-RR HH.MI.SSXFF AM
NLS_TIME_TZ_FORMAT HH.MI.SSXFF AM TZR
NLS_TIMESTAMP_TZ_FORMAT DD-MON-RR HH.MI.SSXFF AM TZR
NLS_DUAL_CURRENCY $
NLS_COMP BINARY
NLS_LENGTH_SEMANTICS BYTE
NLS_NCHAR_CONV_EXCP FALSE
NLS_RDBMS_VERSION 10.2.0.5.0
20 rows selected.
K: Oracle esetében a string-literálokban használhatok
hexa-szekvenciákat? V: Általában nem, de az
unistr
függvénnyel igen. K: És mi ennek a függvénynek az inverze? V: Még nem találtam ilyet, addig is össze lehet csapni valamit házilag:
function nchar_to_unistr (p_str in nvarchar2) return varchar2 is
v_out varchar2(32767);
i pls_integer;
v_chr nchar;
v_code pls_integer;
v_hex varchar2(8);
begin
v_out:= '';
for i in 1..length(p_str) loop
if i = 1 then v_out := 'unistr('''; end if;
v_chr := substr (p_str, i, 1);
if v_chr between ' ' and '~' and v_chr not in ('''', '"', '\') then
v_out := v_out || v_chr;
else
v_code := ascii (v_chr);
v_hex := to_char (v_code, '0xxxx');
v_hex := substr (v_hex, length (v_hex)-3, 4);
v_out := v_out || '\' || v_hex;
end if;
end loop;
if length(v_out)>0 then v_out := v_out || ''')';
else v_out := '''';
end if;
return v_out;
end;
K: Ugorjunk most a Pro*C-re:
Igaz, hogy a NLS_LANG állása befolyásolja a preccompiler működését? V: Hogyne, például ellenőrizheti a forrásprogramot,
hogy UTF-8-valid-e, ha úgy áll a NLS_LANG.
Ha ezt elkerülnéd, akkor pl. ilyesmit írj a Makefile-ba:
K: Miért nem magyarra állítjuk a nyelvet? V: Megtehetjük, ha fordító félreértéseire vagyunk kiváncsiak
a program valódi üzenetei helyett... K: És mit tegyek, ha Windowst használok? V: Programozáshoz?!
K: Ha jól értem,
OCI-t használva mezőnként állíthatom a karakterkészletet (konverziót).
Pro*C-ben is van erre mód? V: A lehetőségek egy kicsit szűkebbek: először is, az első adatbázis-műveletnél
beolvassa a NLS_LANG és NLS_NCHAR környezeti változókat,
és minden kontextusban azokat használja (még akkor is, ha új CONTEXT-et hozol létre). K: Ez miért baj? V: Baj csak akkor lesz, ha különböző komponenseket akarunk egy executable-programba összegyúrni,
és mondjuk az egyik pl. EE8ISO8859P2/AL16UTF16 kódolást szeretne, a másik meg AL32UTF8/AL32UTF8-at. K: Mi is lenne ez a két kódolás az előző válaszban? V: Pro*C-ben a host-változók deklarációjától függ, hogy milyen kódolás menjen a dróton,
illetve milyen kódolás legyen a változóban (ez a kettő persze különbözhet,
a konverziót az Oracle kliensprogramja végzi).
Megpróbálom egy táblázattal szemlélteni:
változódeklaráció dróton változó tartalma
----------------- ------ ---------------
varchar NLS_CHARACTERSET NLS_LANG
varchar CHARACTER SET IS NCHAR_CS NLS_NCHAR_CHARACTERSET NLS_NCHAR
uvarchar NLS_NCHAR_CHARACTERSET UTF16
Megjegyzés: ha nincs NLS_NCHAR, akkor ahelyett is NLS_LANG érvényesül;
ha nincs NLS_LANG, akkor 7-bites ASCII-t kapunk.
K: Az Oracle szerveren milyen karakter-konverzió történik? V: Csak NLS_CHARACTERSET és NLS_NCHAR_CHARACTERSET közötti konverzió,
ha szükség van rá (vagyis NCHAR/NVARCHAR/NCLOB mezőt a kliens "simán" akar kapni/küldeni;
vagy fordítva, sima CHAR/VARCHAR/CLOB mezőt a kliens NCHAR-osan akar kapni/küldeni). K: És ha ezen konverzió során karakterek vesznek el? V: Akkor karakterek vesznek el.
K: És minden más konverzió a kliensen történik? V: Igen. K: És ha olyan szűkített képességű kliensem van, ami nem ismeri a szerver
(egzotikus) kódkészletét, akkor mit tegyek? V: Akkor azt nem tudod használni. Szerezzél egy okosabb klienst!
(Még az InstantClient-nek is van mindenféle kódolást ismerő változata.)
K: Java-kliens (JDBC) esetén van valami különösebb aggódnivaló? V: Például programfutáskor problémát okozhat, ha az
orai18n.jar nem szerepel a classpath-on.
Előfordulhat, hogy nincs hibaüzenet, "csak" hibás adatokat olvasunk ki az adatbázisból.
Adott esetben elhelyezhetünk a 'main'-ben egy ilyen ellenőrzést:
if (oracle.jdbc.driver.OracleDriver.class.getClassLoader().
getResource ("oracle/i18n/data/lx20020.glb") == null) {
System.out.println ("Úgy érzem, hogy nincs meg az orai18n.jar -- kilépek");
return;
}
K: JDBC-ről szólva, mi a különbség
setString
és
setNString,
illetve
getString
és
getNString
között? V: Az előbbi esetben ettől függ, hogy a dróton NLS_CHARACTERSET vagy NLS_NCHAR_CHARACTERSET
menjen-e; NCHAR-os mezők esetén az utóbbi ajánlott (az előbbi a szerver beállításától függően
karaktervesztést okozhat, pl. EE8ISO8859P2-be nem férnek bele a cirill/görög betűk).
Az utóbbi esetben nem látok különbséget, ugyanis a getString/getNString akkor fut,
amikor az adat már megérkezett a kliensre, ahol fixen UTF-16-ra konvertálódik.
K: JAVA/JDBC-esetén van-e valamilyen különleges szabály a NLS_LANG beállítására? V: Ha méréseim nem csalnak, tetszés szerint beállíthatod, nem lesz hatása.
Helyette a user.language és a user.country használható,
vagyis azok alapján állítja be az Oracle a NLS_LANGUAGE és a NLS_TERRITORY értékét. Pl.:
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.
K: Mifene az az FPDF? V: Egy PHP modul, PDF-ek előállítására szolgál. K: Hogyan és hová kell telepíteni? V: Erről nincs sok szó az install.txt-ben sem, a legjobb ötletem az,
hogy a letöltött tgz/zip tartalmát helyezd el a
/usr/local/share/php/fpdf könyvtárba
(azon belül lesznek az fpdf.php, fpdf.css, stb. fájlok,
és a doc, font, makefont, tutorial alkönyvtárak).
K: És a PHP meg fogja ott találni? V: Az a php.ini fájl beállításaitól függ, pontosabban az
include_path-tól;
illetve használhatod a
set_include_path
függvényt is. K: Egyéb tudnivaló a telepítésről? V: Ha a PHP-d 5.3.x-es, vagy újabb, akkor távolítsd el a fpdf.php-ből a megszűnt
get/set_magic_quotes_runtime
hívásokat.
K: No igen, és hogy lesznek az ezzel előállított PDF-ben helyes magyar ékezetek? V: Ahhoz, hogy a
SetFont-tal
kiválaszthass egy fontot, kell legyen egy megfelelő
fontnév.php fájl
a */fpdf/font könyvtárban. A gyárilag szállított fájlok cp1252-hez
(vagyis nyugat-európai betűkhöz) vannak kialakítva.
K: És honnan lesznek a cp1250-es
(kelet-európai betűs) fájlok? V: Ehhez a mellékelt makefont.php-t
kell használni, és kellenek hozzá a Windows-os ttf fájlok is. Példa:
FPDF=/usr/local/share/php/fpdf # vagy ahol van
WINFONT='/C/Windows/Fonts' # vagy ahol vannak a fontok
for FONT in arial ariali arialbd arialbi; do
php $FPDF/makefont/makefont.php "$WINFONT/$FONT.ttf" cp1250 false
done
K: Ugyanez Windows-on hogy lenne? V: Értelmszerűen módosítani kell a scriptet
(vagyis batch-et, Windows-os szóval),
valami ilyesmi lehetne:
@echo off
set FPDF=C:\PHP-EXT\FPDF
set WINFONT=C:\Windows\Fonts
cd %FPDF%\font
mkdir cp1250 2>nul:
cd cp1250
for %%I in (%WINFONT%\*.ttf) do php %FPDF%/makefont/makefont.php %%I cp1250 false
Megj: További Windows-os információkért lapozz vissza
ide.
K: Ezt minden futásnál újra meg újra meg kell csinálni? V: Nem, a fájlok újrahasznosíthatóak;
több megoldás képzelhető el, az egyik ez: az fpdf font-könyvtárában
készítsünk egy cp1250 nevű alkönyvtárat,
és oda helyezzük el a létrehozott *.php fájlokat.
FPDF=/usr/local/share/php/fpdf # vagy ahol van
WINFONT='/C/Windows/Fonts' # vagy ahol vannak a fontok
cd $FPDF/font
mkdir cp1250
cd cp1250
for i in "$WINFONT"/*.ttf; do # vagy nagybetűvel: *.TTF
php $FPDF/makefont/makefont.php "$i" cp1250 false
done
K: És a
SetFont
automatikusan az új fájlokat fogja használni? V: Nem, ehhez kell még egy
AddFont is. Komplett példa:
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: 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 as ékezet/szóköz, mert az jelenik meg a felhasználó
böngészőjében, akkor magyarázzuk el neki,
hogy a fájlnév egyáltalán nem arra való,
hogy megjelenjen a felhasználó böngészőjében.
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 byte (base64 nélkül 24 byte):
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8A
K: Ha történetesen C-programból szeretnék
kulcspárt generálni, milyen függvényeket használhatok? V: Ezeket javasolnám:
RSA_generate_key,i2d_RSAPrivateKey ('prv'-hez),
i2d_RSAPublicKey ('rsapub'-hoz),
i2d_RSA_PUBKEY ('pub'-hoz).
Az utóbbi három leírása
itt olvasható.
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: 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 ...
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.