Obsah
1. Základ práce s řetězci
2. Regulární výrazy
3. Funkce pro práci s řetězci
4. Řetězce a příkaz switch
5. Práce se soubory
6. Systémově orientované příkazy pro práci se soubory
7. Síťově orientované příkazy
8. Další volně dostupné moduly
9. Obsah dalšího pokračování tohoto seriálu
1. Základ práce s řetězci
Programovací jazyk Tcl obsahuje velkou podporu pro práci s řetězci. Je to ostatně logické – všechny proměnné i příkazy jsou interně v tomto jazyku řetězcem reprezentovány, proto je pro jejich snadné a rychlé zpracování zapotřebí používat větší množství sofistikovaných funkcí. Před popisem jednotlivých funkcí si řekneme důležitou a užitečnou informaci – poslední hlavní verze Tcl (tj. verze 8.x) plně podporuje kódování Unicode, ve všech řetězcích je tedy možné používat znaky prakticky všech světových abeced (samozřejmě včetně češtiny) – Tcl je tedy pro práci s řetězci výhodnější než dnes mnohdy mylně upřednostňovaný Perl :-). Dokonce je možné (příliš to však nedoporučuji) používat češtinu pro pojmenování proměnných a funkcí – viz následující příklad:
proc čeština {} {
set českýtext "příliš žluťoučký kůň úpěl ďábelské ódy"
puts $českýtext
}
# zavoláme funkci čeština:
čeština
Mnoho funkcí, které v Tcl pracují s řetězci, se opírá o takzvané regulární výrazy. Popis regulárních výrazů v mnohém přesahuje kapacitu tohoto článku (je dokonce jediným tématem několika rozsáhlých knih), proto si ve druhé kapitole povíme pouze nejnutnější pravidla, která se nám mohou při programování v Tcl hodit. Regulární výrazy totiž bezesporu představují jeden z nejsilnějších programových nástrojů velkého množství dynamických skriptovacích jazyků.
2. Regulární výrazy
V Tcl jsou podporovány dva typy regulárních výrazů. První typ, nazývaný také glob matching, v mnohém odpovídá regulárním výrazům známým z unixového shellu (typické použití tohoto typu regulárních výrazů představují příkazy ls, mv, cp atd.). Druhý typ regulárních výrazů, který je složitější (už jen tím, že používá více „magických“ znaků), zhruba odpovídá regulárním výrazům použitým v textovém editoru Vim, vyhledávací utilitě grep atd.
V regulárních výrazech prvního typu se mohou vyskytovat následující zástupné znaky:
Znak | Význam znaku |
* | libovolně dlouhá sekvence znaků |
? | libovolný znak (pouze jeden) |
[…] | libovolný znak ze zadané množiny (lze použít i interval – viz příkazy shellu) |
\<znak> | odstranění speciálního významu výše zmíněných zástupných znaků |
další znaky | další znaky se s řetězcem porovnávají bez dalšího zpracování |
Regulární výrazy druhého typu jsou použity například v dále uvedených příkazech regexp a regsub. Možností pro specifikaci řetězců zde existuje více, význam některých magických znaků se dokonce změnil (hvězdička atd.):
Znak | Význam znaku |
. | libovolný znak |
* | nula či více výskytů předchozí položky |
+ | jeden či více výskytů předchozí položky |
| | volba mezi dvěma výrazy (or) |
[…] | libovolný znak ze zadané množiny (lze použít i interval – viz příkazy shellu) |
^ | začátek řetězce (uvnitř závorek funguje jako negace) |
$ | konec řetězce |
\<znak> | odstranění speciálního významu zástupného znaku |
další znaky | další znaky se s řetězcem porovnávají bez dalšího zpracování |
3. Funkce pro práci s řetězci
Funkcí pro manipulaci s řetězci existuje v programovacím jazyku Tcl značné množství. Tyto funkce si můžeme rozdělit do dvou kategorií – funkce volané přímo svým jménem a „podpříkazy“ funkce string. Nejprve si uvedeme, které funkce pro práci s řetězci je možné volat přímo:
Název funkce | Význam funkce |
---|---|
append | pomocí této funkce je možné přidat znaky na konec řetězce |
subst | tento příkaz vnucuje substituci proměnných a příkazů |
string | funkce pro manipulaci s řetězcem s mnoha dalšími volbami, které budou uvedeny v dalším textu |
regexp | vyhledání shody v řetězci podle zadaného regulárního výrazu (zde se používá kompletní repertoár značek v regulárním výrazu) |
regsub | vyhledání shody v řetězci podle zadaného regulárního výrazu a zápis nového řetězce do zadané proměnné |
format | formátování řetězce ve stylu C-čkovské funkce sprintf() |
scan | čtení hodnot z řetězce ve stylu C-čkovské funkce sscanf() |
binary | formátování a získávání informací z takzvaných binárních řetězců (neprovádí se interní konverze do a z Unicode) |
V předchozí tabulce jsme si mimo jiné uvedli i funkci string. Tato funkce slouží k mnoha manipulacím s řetězci, proto také obsahuje mnoho „podpříkazů“, podobně jako minule zmíněná funkce array při práci s poli. V níže uvedené tabulce si uvedeme některé základní manipulace s řetězci, které lze provádět právě pomocí funkce string:
Název funkce | Význam funkce |
---|---|
string length | tato funkce vrací, jak již samotný název napovídá, délku řetězce |
string index | pomocí této funkce lze získat znak na určité pozici, přičemž první znak má, podobně jako v C-čku, index nulový |
string first | vyhledávání podřetězce v řetězci směrem od začátku (odpovídá C-čkovské funkci strstr()) |
string last | vyhledávání podřetězce v řetězci směrem od konce |
string compare | lexikografické porovnání dvou řetězců (odpovídá C-čkovské funkci strcmp(), výstupní hodnoty jsou –1, 0 a 1) |
string match | porovnání řetězce se vzorem, přičemž je možné použít zjednodušených regulárních výrazů (podobně jako v shellu, výstupní hodnoty jsou 0 nebo 1) |
string range | tato funkce vrací podřetězec zvolený dvěma indexy. Místo horního indexu lze použít i slovo end (to se vyhodnotí jako expr [string length] –1) |
string tolower | tato funkce vrací řetězec odpovídající původnímu řetězci, přičemž se provádí převod na malá písmena – minusky (funguje i pro češtinu) |
string toupper | obdoba předchozí funkce s tím, že se zde provádí převod na kapitálky (opět podporuje češtinu) |
string trim | tato funkce vrací podřetězec, ze kterého jsou odstraněny vybrané počáteční a koncové znaky |
string trimleft | obdoba předchozí funkce s tím rozdílem, že se odstraňují pouze počáteční znaky |
string trimright | odstranění pouze koncových znaků |
string wordstart | tato funkce vrací index prvního znaku slova, které obsahuje zadanou pozici |
string wordend | obdoba předchozí funkce, zde se však vrací index znaku, jež se nachází ihned za slovem |
4. Řetězce a příkaz switch
Příkaz switch jsme si ukazovali v předchozím dílu tohoto seriálu. Zde si jenom připomeňme, že je možné ho zapsat podle následujícího schématu:
switch řetězec vzor1 větev1 vzor2 větev2 ... default větev_n
Na rozdíl od C-čka a od něj odvozených programovacích jazyků se nemusí jednotlivé větve ukončovat žádným příkazem typu break – vždy se provede maximálně jedna větev. Větev označená slovem default je přitom brána jako implicitní, tj. pokud není splněna žádná z podmínek, provede se právě tato část programu. Nyní se vraťme k podmínkám, které je možné v příkazu switch použít. Nejedná se o klasické booleovské podmínky, ale obecně o regulární příkaz, který slouží k porovnání libovolného řetězce se zadanými vzory. Kromě toho je možné příkazu switch zadat několik voleb, kterými se ovlivňuje způsob porovnání řetězců. Jedná se o následující volby:
Volba | Význam volby |
---|---|
-exact | exaktní porovnávání řetězce se vzorem (implicitní nastavení) |
-glob | porovnávání na základě jednodušší formy regulárního výrazu (jako v shellu) |
-regexp | porovnávání na základě rozšířené formy regulárního výrazu (jako ve Vimu) |
Ukažme si nyní příklady použití příkazu switch:
# příkaz switch na jednom řádku s exaktním porovnáváním
set foo "abc"
switch abc a {expr 1 } b {expr 1} $foo {expr 2} default {expr 3}
# příkaz switch rozepsaný na více řádků se shellovským prohledáváním
switch -glob aaab {
a*b {expr 1}
b {expr 1}
a* {expr 2}
default {expr 3}
}
# spojení dvou větví (obdoba C-čkovského stylu psaní více větví):
switch -glob aaab {
a*b -
b {expr 1}
a* {expr 2}
default {expr 3}
}
5. Práce se soubory
Základní práce se soubory je v Tcl velmi jednoduchá a přímočará. K dispozici jsou funkce pro otevření souboru, zavření souboru a pro čtení a zápis dat z a do souboru. Při běžné práci se k souborům přistupuje jako k datovým tokům (data stream) – v dokumentaci Tcl se pro ně používá výraz kanály. Podobně jako v jiných programovacích jazycích se i zde při spuštění aplikace otevřou tři implicitní kanály: stdin (standardní vstup), stdout (standardní výstup) a stderr (chybový výstup). Samozřejmě, že na systémové úrovni funguje přesměrování těchto kanálů, používání rour atd.
Mimo implicitní kanály je možné otevřít kanály další. Pro tento účel se používá příkaz open, který se volá s následujícími parametry:
open jménoSouboru přístup práva
Jméno souboru může být zadáno buď s absolutní, nebo s relativní cestou. V řetězci „přístup“ je specifikováno, zda se soubor otevře pro čtení (read), zápis (write), či přidání na konec souboru (append). Hodnota tohoto řetězce je stejná jako u C-čkovské funkce fopen(). Řetězec „práva“ je možné použít při vytvoření nového souboru (tj. otevření neexistujícího souboru pro zápis). Tímto řetězcem se určují výchozí práva k souboru (viz příkaz chmod). Pokud otevření souboru proběhne v pořádku, vrátí se interní hodnota otevřeného kanálu. Tato hodnota se využívá v následujících funkcích.
Po přečtení či zápisu všech požadovaných dat je vhodné kanál uzavřít. Pro tento účel je k dispozici příkaz close, který se volá následovně:
close kanál
Jediným argumentem tohoto příkazu je interní hodnota otevřeného kanálu, kterou vrátil výše popsaný příkaz open.
Je samozřejmé, že kromě otevření a zavření souborů potřebujeme načítat ze souborů data či do nich ukládat. K tomuto účelu existuje množství funkcí, my si zde ukážeme pouze funkce nejzákladnější. Budeme se zabývat hlavně textovými soubory, jejichž použití je při práci v Unixu nejčastější. První funkcí je gets, která slouží k načtení celého řádku ze souboru otevřeného pro čtení:
gets kanál proměnná
Tato funkce vrátí počet načtených znaků. Pokud narazí na konec souboru, vrátí hodnotu –1. Všimněte si, že není zapotřebí specifikovat maximální délku řetězce (ta je teoreticky neomezená), takže chyby typu „buffer overflow“ by zde neměly nastat. Opakem funkce gets je funkce puts, jež slouží k zápisu celého řetězce do souboru. Funkce se může volat s nepovinným parametrem -nonewline, kterým je možné zakázat zápis znaku pro odřádkování (ten totiž může být součástí řetězce):
puts kanál řetězec
puts -nonewline kanál řetězec
Typickým příkladem ve většině příruček k programovacím jazykům je funkce pro zkopírování obsahu souboru. Tuto funkci je možné v Tcl zapsat následovně:
proc copyFile { inFileName outFileName } {
set inFile [open $inFileName "r"]
set outFile [open $outFileName "w"]
while { [gets $inFile temp] != -1 } {
puts $outFile $temp
}
close $inFile
close $outFile
}
Kromě toho je pro čtení ze souboru možné použít funkci read, která načítá zadaný počet znaků (tuto funkci je možné aplikovat i na binární soubory, protože se nepřevádí znaky pro konce řádků). Pokud není zadán maximální počet znaků, načte se veškerý obsah souboru:
read kanál početZnaků
read kanál
Příkazů pro práci se soubory je samozřejmě více, jejich seznam je uveden v následující tabulce:
Název funkce | Význam funkce |
---|---|
open | otevření souboru pro čtení/zápis/přidání na konec |
close | uzavření souboru |
read | čtení bytů ze souboru |
gets | načtení řádku ze souboru |
puts | zápis řetězce do souboru |
format | formátovaný zápis do řetězce, většinou se používá spolu s funkcí puts |
scan | čtení hodnot z řetězce, většinou se používá spolu s funkcí gets |
6. Systémově orientované příkazy pro práci se soubory
V následující tabulce jsou uvedeny funkce, které pracují se soubory jako celky, tj. nikoli s jejich obsahem. Jedná se o funkce, které jsou do určité míry systémově orientované (a nemusí tak být nutně přenositelné na různé platformy).
Název funkce | Význam funkce |
---|---|
file size soubor | vrací délku souboru v bytech |
file atime soubor | vrací poslední čas přístupu k souboru |
file mtime soubor | vrací čas poslední úpravy souboru |
file dirname soubor | vrací část jména souboru reprezentujícího adresář (na Unixech jde o rozdělení řetězce před posledním lomítkem) |
file extension soubor | vrací příponu souboru (na Unixech se jedná o rozdělení řetězce před poslední tečkou) |
file rootname soubor | vrací jméno souboru bez přípony |
file exists soubor | vrací hodnotu 1, pokud soubor existuje |
file isdirectory soubor | vrací hodnotu 1, pokud je názvem adesář (v newspeaku složka :-) |
file isfile soubor | vrací hodnotu 1, pokud je názvem běžný soubor (ne adresář ani roura) |
file readable soubor | vrací hodnotu 1, pokud lze soubor číst (test práv uživatele) |
file writable soubor | vrací hodnotu 1, pokud lze do souboru zapisovat (test práv uživatele) |
file executable soubor | vrací hodnotu 1, pokud je soubor spustitelný (pro daného uživatele) |
7. Síťově orientované příkazy
Práce se sítí je v Tcl velmi jednoduchá, zejména v porovnání s dostupnými příkazy v C-čkových knihovnách. Základem komunikace po síti jsou zde sockety, které se chovají podobně jako soubory (resp. datové kanály) – lze je otevírat, zavírat, číst z nich data a naopak do nich data zapisovat. U socketů se rozlišuje mezi klientem a serverem, většinou se však komunikace nastavuje tak, že server naslouchá požadavkům klienta a posléze tyto požadavky plní.
Na straně klienta se socket otevírá příkazem:
socket host port
Kde host je jméno serveru a port číslo TCP portu, na kterém server naslouchá.
Na straně serveru se používá stejný příkaz, ale s jedním volitelným parametrem navíc:
socket -server příkaz port
Kde příkaz nastaví obslužnou rutinu, která se zavolá v případě, že se k serveru připojí nějaký klient. Vyvolání rutiny proběhne asynchronně k běhu programu. Pokud je zapotřebí program během požadavku pozastavit a požadavek obsloužit, je možné použít příkaz vwait, který akceptuje jeden parametr, jímž je kanál vytvořený příkazem socket.
8. Další volně dostupné moduly
K programovacímu jazyku Tcl je dodáváno poměrně velké množství rozšiřujících modulů – ty jsou dostupné ve formě knihoven. Jedním z nejznámějších a nejpoužívanějších rozšíření Tcl je program expect Dona Libese, který slouží k automatizaci řízení interaktivních programů. Tento program je ideální pro testování programů, inicializaci modemového připojení (různé reakce na stavy linky při připojování) atd.
Dalším rozšířením je knihovna BLT určená zejména pro práci s grafikou, [incr Tcl] je zase objektové rozšíření původního procedurálního Tcl (všimněte si pěkné slovní hříčky, která paroduje název C++ – oba názvy mají v daném jazyku korektní syntaxi a stejnou sémantiku).
Absolutně nejznámějším modulem je však knihovna Tk pro tvorbu grafického uživatelského rozhraní, kterou se budu podrobněji zabývat ve zbývajících částech tohoto seriálu.
9. Obsah dalšího pokračování tohoto seriálu
V dalším pokračování tohoto seriálu se již budeme zabývat knihovnou Tk, která slouží k jednoduchému a rychlému návrhu grafického uživatelského rozhraní (GUI). Tato knihovna byla v minulosti důvodem velké oblíbenosti Tcl, proto se také můžeme často setkat se spojením Tcl/Tk.