Práce s textem

Občas potřebujeme pracovat s textem více než jen ho spojit. Budeme potřebovat text rozdělit, formátovat apod. K tomu slouží třída string, která je zabudována v základu Lua a kterou si dnes rozebereme. Tyto funkce můžete používat pro práci s texty pro hru, hodnoty v XML které jsou zapsány jako text oddělený mezerou atd... V tomto článku vynechám funkce které pracují s bitovými kódy písmen - nemusí sedět na linuxu a zároveň windowsu, takže pro hru nepoužitelné.

string.format()

Tato funkce je velmi užitečná a je mocným nástrojem v úpravě textů. Lze tak ošetřit hodnota nil při výpisu nápověď atd... Vrací nám text s tím, že zástupné znaky začínající % nahradí v daném pořadí hodnotami z zdalších parametrů. Jako první zde uvedu tabulku zástupných znaků.

Zástupný znak Význam
%c Očekáváno číslo jako parametr. Zástupný znak nahrazen znakem odpovídající číselnému kódu v parametru.
%d Očekáváno číslo jako parametr. Zástupný znak nahrazen znakem číslem, které je v parametru.
%E Očekáváno číslo jako parametr. Zástupný znak nahrazen znakem číslem, které je v parametru s tím, že na konec přidá exponent jako velké E.
%e Stejné jako velké E až na to, že místo E v eponentu bude e.
%f Desetinné číslo s dlouhým rozvojem.
%g Desetinné číslo s kratším rozvojem. Jinak v tom nevidím rozdíl oproti %f.
%i Celé číslo - stejné jako %d.
%u Nezáporné celé číslo. Pokud bude číslo záporné bude dělat blbosti.
%o Číslo v oktálové (osmičkové) podobě. Dle mého má minimální využití.
%x Hexadecimální (šestnáctková) podoba čísla s malými písmeny. Dle mého nemá využití.
%X Hexadecimální (šestnáctková) podoba čísla s velkými písmeny. Dle mého nemá využití.
%q Prostý text v uvozovkách.
%s Prostý text bez uvozovek.

Toto je seznam základních parametrů pro string.format(...);. Dají se pak využít i u jiných funkcí které je podporují. Jejich výhodou je i možnost kombinovat je s čísly a hrát si s nima. Zde ukázka základu:

1
2-- promenne s hodnotami
3local text = "Ahoj cislo je %d";
4local cislo = 54335;
5local desetinne = 5.678464;
6local znak = 65;
7local text2 = "Textik";
8
9-- vypisy
10print(string.format("Cele cisla: %d, %i, %u, %u",cislo,6543,6313,-6313));
11-- vystup -> Cele cisla: 54335, 6543, 6313, 18446744073709545303
12
13print(string.format("Desetinne cisla: %f, %g",desetinne,desetinne));
14-- vystup -> Desetinne cisla: 5.678464, 5.67846
15
16print(string.format("Exponencialni rozvoje: %e, %E",desetinne,desetinne));
17-- vystup -> Exponencialni rozvoje: 5.678464e+000, 5.678464E+000
18
19print(string.format("Jine tvary cisel: %o, %x, %X",cislo,cislo,cislo));
20-- vystup -> Jine tvary cisel: 152077, d43f, D43F
21
22print(string.format("Text jako vystup: %s, %q, %c",text2,"Ahoj ja jsem v uvozovkach",znak));
23-- vystup -> Text jako vystup: Textik, "Ahoj ja jsem v uvozovkach", A
24
25print(string.format(text,cislo));
26-- vystup -> Ahoj cislo je 54335

Všimněte si, že proměnná která má v textu zástupný parametr ho změní také pokud ji formátujeme. To je značná výhoda

Omezování parametrů

Určitě jste si ve hře všimli, že né všechny desetinná čásla se vypisují celá, ale třeba zaokrouhlená jen na 2 desetinná místa. Komu by se taky chtělo číst nevzhledné sedmimístné desetinné číslo? A právě k takovému "zhezčení" slouží omezení parametrů. Funguje pro číselné parametry %f a %g. Omezení čísla se dělá tak, že mezi procento (%) a zástupný znak se vloží číslo, které reprezentuje jeho délku. Příkladem je parametr %.2f, který zajistí to, že výsledné číslo bude "oříznuto" dvě cifry za desetinnou čárkou.

Příklad:

1
2-- Skareda desetinna promenna s celociselnou
3local desetinna = 18646.68468416146463416;
4
5-- vypis
6print(string.format("Desetinna omezena: %.2f",desetinna));

string.find();

Funkce string.find(); se používá zejména u instalačních skriptů, ale využít e dá i jinde. Vstupními parametry je řetězec (text), hledaná fráze, nepovinný index začátku, čtvrtý parametr si ukážeme někdy později. Funkce dělá to že hledá danou frázi v zadaném řetězci od daného indexu a vrací nám dvojici proměnných udávajících start a konnec fráze v textu. Sice nám funkce nevrátí všechny výskyty, ale díky vracenému parametru konce se můžeme posunou dopředu a pokračovat v hledání další fráze.

Ukázkový příklad string.find();:

1-- Nas text
2local text = "Ahoj, ja jsem hodne dlouhy text ve kterem budeme neco hledat.";
3
4-- ulozeni do promennych
5local start,konec = string.find(text,"te");
6print("Vystup prvniho te v retezci: "..string.format("%d, %d",start,konec));
7
8-- vypis primo
9print("Vystup prvniho te v retezci: "..string.format("%d, %d",string.find(text,"te")));
10
11-- vsechny "te" v retezci
12start = nil;
13konec = 1;
14repeat
15 start,konec = string.find(text,"te",konec);
16 if start ~= nil then
17 print("Vystup te v retezci: "..string.format("%d, %d",start,konec));
18 end;
19until start == nil;
20

string.upper() a string.lower()

Tyto dvě funkce mají jeden vstupní parametr - text - a jeden výstup - taky text. string.upper(); změní všechna písmena na velká. Naopak string.lower(); změní všechna písmena na malá. Jednoduchý příklad ukáže.

1
2print(string.upper("jsem moc velky"));
3-- V logu: JSEM MOC VELKY
4
5print(string.lower("A JA JSEM MOC MALY"));
6-- V logu: a ja jsem moc maly
7

string.gsub();

Jedná se o velmi komplexní funkce a proto doufám že ji pochopíte. Funkce má vstrupní parametry řetězec, hledaný výraz, přepisovací výraz a nepovinný počet provedení. Funkce projde řetězec a nahradí každý hledaný výraz výrazem přepisovacím. Velice jednoduchý příklad použití funkce:

1
2-- promenne rovnou s funkci
3local vysledek,pocetZmen = string.gsub("Hello banana", "banana", "Lua user");
4
5-- vysledny text
6print(vysledek);
7-- zobrazeni poctu zmen
8print("Pocet zmen pred vypsanim vylsdku: "..pocetZmen);
9--[[
10Vysledek v logu bude:
11
12Hello Lua user
13Pocet zmen pred vypsanim vylsdku: 1
14
15]]--
16

Nyní si ukážeme použití nepovinného parametru:

1
2-- promenne rovnou s funkci
3local maxZmen = 3;
4local vysledek,pocetZmen = string.gsub("Ahoj ja jsem retezec s moc j", "j", "x",maxZmen);
5
6-- vysledny text
7print(vysledek);
8-- zobrazeni poctu zmen
9print("Pocet zmen pred vypsanim vylsdku: "..pocetZmen);
10--[[
11Vysledek v logu bude:
12
13Ahox xa xsem retezec s moc j
14Pocet zmen pred vypsanim vylsdku: 3
15
16]]--
17

Toto jse lehké že? Nyní se tedy kouknem na něco složitějšícho - použití předávání parametrů u této funkce. Funguje to tak, že obalíme hledaný výraz závorkami a podle pořadí závorek máme k dispozici parametry %1, %2, %3 ... %n. Ty pak můžeme použít pro znovuvypsání s nějakým dodatkem. Ukážu:

1
2-- jednoduche s jednim parametrem
3local vysledek,pocetZmen = string.gsub("Hello banana", "(banana)", "Lua user - or maybe %1?");
4
5
6print(vysledek);
7-- Hello Lua user - or maybe banana?
8
9print("Pocet zmen pred vypsanim vysledku: "..pocetZmen);
10-- Pocet zmen pred vypsanim vysledku: 1
11
12-- kombinace vice parametru (otoceni pismenek)
13print(string.format("%s",string.gsub("oh oh oh", "(o)(h)", "%2%1")));
14-- ho ho ho
15

Toto bylo použití takového speciálního zástupného znaku - ale co když potřebujeme řetězec zpracovat ještě více a nestačí nám k tomu toto? Co když mám řetězec s instrukcemi a chci je spouštět. Možná že to nikdy nevyužijete, ale člověk nikdy neví. Tato metoda se dělá tak, že místo přepisovacího parametru použiju anonymní funkci. To je funkce která nemá název a volá se přímo na řádku k nějaké činnosti. Na příkladu pochopíte:

1
2-- nulovani promenne
3local pocetE = 0;
4
5-- pouziti anonymni funkce jako parametr
6local vysledek,_ = string.gsub("Ahoj ja jsme tvuj retezec.","e",function(znak)
7 -- tu je kod anonymni funkce
8 pocetE = pocetE + 1;
9 -- a vracim znak - muzu vratit i neco jineho
10 -- toto pocita pismena v retezci
11 return znak;
12end
13);
14
15-- vypsani textu
16print(vysledek);
17-- vypsani poctu znaku - muzu pouzit i navratovou hodnotu
18-- ale zde si ukazujeme kod v anonymni funkci
19print(pocetE);
20

Tak a nyní si ukážeme použití zachytávání jakkoli dlouhého textu mezi nějakými zástupnými znaky. K tomu se používá dvou konstruncí:

  1. (.-) - zachytí minimální počet znaků mezi zástupnými znaky. Příklad: {(.-)} zachytí všechny znaky mezi složenými závorky.

  2. (.*) - zachytí maximální počet znaků mezi zástupnými znaky. Příklad: {(.*)} zachytí všechny znaky od první do poslední složené závorky v textu.

Nyní si tyto konstrukce ukážeme na příkladu.

1
2-- text
3local text = "Ahoj ja jsem tvuj text {a jsem velmi dlouhy}";
4
5-- nahrazeni textu necim jinym
6local v,_ = string.gsub(text,"{(.-)}",function(a)
7 return string.upper(a);
8end
9);
10
11-- vypis vysledku
12print(v);
13-- log: Ahoj ja jsem tvuj text A JSEM VELMI DLOUHY
14

string.len();

Funkce má jeden vstupní parametr - text. Jako výstup vrací délku zadaného textu (počet znaků).

1
2-- ulozeni do promenne
3local delka = string.len("Vitej programatore");
4print(delka);
5-- v logu: "18"
6

string.rep();

Funkce opakuje řetězec nkrát. Vtupnímy parametry je text a kolikrát se má opakovat. Výsledkem je řetězec který se buď ukládá do proměnné nebo rovnou vypisuje.

1
2-- opakovani a rovnou ulozeni do promenne
3local vysledek = string.rep("Vitejte v LUA!\n",5);
4
5print(vysledek);
6--[[
7LOG:
8Vitejte v LUA!
9Vitejte v LUA!
10Vitejte v LUA!
11Vitejte v LUA!
12Vitejte v LUA!
13
14]]--
15

Všimněte si, že aby se mi to nezkopírovalo za sebe na řádek, použil jsem zástupný znak pro enter a to sice . K podobným znakům se budeme dostávat v průběhu.

string.sub();

Tato funkce vrací výňatek z textu. Vstupnímy parametry je text, Začátek výběru a nepovinný konec výběru. Pokud není určen konec výběru, funkce vrátí vše od začátku výběru až po konec textu. Ukážu příklad.

1
2-- Bez koncoveho parametru
3local vynatek = string.sub("Ahoj priteli muj.",6);
4print(vynatek);
5-- "priteli muj."
6
7-- S koncovym parametrem
8local vynatek = string.sub("Ahoj priteli muj.",6,12);
9print(vynatek);
10-- "priteli"
11

Všimněte si, že vybrané znaky jsou s parametry včetně. Tudíž když do prvního parametru zadám číslo 1, bude se začínat prvním písmenem.

string.reverse();

Vrátí text obráceně. Jediný vstup je text a výstupem je ten samý text, akorát obráceně. Jednoduchá ukázka:

1
2local obracene = string.reverse("Kuna nese nanuk");
3print(obracene);
4-- "kunan esen anuK"

string.match();

Hledá text v textu podle zadaných parametrů. Zní to krkolomně ale výsledek je naprosto logický. Můžeme hledat také podle více parametrů naráz. Vstupními proměnnými je řetězec, hledaný text a nepovinný počáteční index. Pro hledaný text platí stejné pravidla jako pro hledaný text ve funkci gsub s tím, že zde se využije více rozšířených podmínek. Rozšířené podmínky jsou %d+ pro číslo delší než 1 znak, %a+ pro text delší než 1 znak (alfanumerický) a %s pro jeden znak textu. Můžeme samozřejmě použít přesné znění hledaného textu, ovšem to tu nelze moc použít, protože většinou chceme hledat obecně. Pokud nenajde řetězec vrátí nil.

1
2local promenna = string.match("Mam tu 5 tun psenice","%d+ %a+ %a+");
3print(promenna);
4-- "5 tun psenice"
5

string.gmatch();

Tato funkce je iterátorem. Proto se používá v generickém cyklu for. Použití je tady stejné jako pairs nebo ipairs. Vrací jednu hodnotu a to sice aktuální platný hledaný výraz, který se pokusí najíc co nejvícekrát v zadaném textu a každou možnost projde. Teoreticky může vracet více hodnot ale to někdy příště. Nyní přílad:

1
2local text = "Ahoj 52, dnes tu mam cisla 12,5,8,62,31";
3local soucet = 0;
4for cislo in string.gmatch(text, "%d+") do -- vyhledam vsechny cisle
5 soucet = soucet + cislo;
6end;
7print("Soucet cisel v textu: "..soucet);
8-- "Soucet cisel v textu: 170"
9

Co se stalo? Prošli jsme text a stím, že jsme hledali každé číslo větší nebo rovno jedné číslici. Každé takové číslo bylo přičteno k celkovému souštu který byl následně po skončení cyklu vypsán.

Související video

Soubory ke stažení

  1. mujskript.lua