Vydělávej až 160.000 Kč měsíčně! Akreditované rekvalifikační kurzy s garancí práce od 0 Kč. Více informací.
Hledáme nové posily do ITnetwork týmu. Podívej se na volné pozice a přidej se do nejagilnější firmy na trhu - Více informací.

Lekce 4 - Řetězce v Qt - QString a QChar

V minulé lekci, Dokončení prvního okna v Qt a C++ - Tlačítko, jsme se posunuli v obsasti formulářových aplikací v Qt a C++ o pořádný krok dále. Uvedli jsme si problematiku layout manažerů, vložili na formulář první komponentu a naučili se také obsluhovat signály pomocí slotů.

Než se pustíme do dalších okenních aplikací, bude dobré si povědět něco málo o třídě QString, která obsahuje velmi výkonné metody a není jich zrovna málo.

QString

Tato třída je schopná pracovat s unicode řetězci, které kóduje do 16-ti bitových znaků. Právě s kódováním textu má jinak C++ problém, na což jste díky tomu, že v češtině používáme háčky a čárky, asi již přišli. Pro tento účel využívá QString třídu QChar a texty ukládá jako pole QChar[].

Předně je celkem pozitivní, že QString je, na rozdíl od řetězců mnoha programovacích jazyků, mutable - tedy obsah objektu můžeme libovolně měnit, např. měnit písmena v textu. Též bych si dovolil trochu se odchýlit od C++ knihovny iostream. Protože si povídáme o Qt frameworku, budeme dnes místo něj používat textový proud na standardní výstup pomocí třídy QTextStream.

A mimo to, iostream nedokáže pracovat s Qt řetězci. Tuším, že unicode zvládá pomocí ukazatele na typ wchar_t. Sami si to můžete ověřit na tomto malém experimentu:

#include <iostream>
#include <QString>

using namespace std;

int main(int argc, char *argv[])
{
    QString text = QString("Ahoj světe! Jak se máš?");

    cout << text << endl;
}

Pokud se tento zdrojový kód pokusíte přeložit, vypadne vám následující chyba:

Konzolová aplikace
Chyba: no match for ‘operator<<’ (operand types are ‘std::ostream {aka std::basic_ostream<char>}’ and ‘QString’)
cout << text << endl;
~~~~~^~~~~~~

Vytvoření projektu

Pojďme se podívat na několik prvních možností. Vytvoříme si v QtCreatoru konzolový projekt, např. s názvem retezce. Soubor main.cpp upravíme do podoby níže. Potom si podrobně projdeme jednotlivé kroky:

#include <QTextStream>
#include <QString>

int main(void) {

    QTextStream out(stdout);

    QString str = "líbí";

    str.append(" ITNetwork!!!");
    str.prepend("Mně se ");

    out << str << endl;
    out << "Retezec obsahuje " << str.count() << " znaku" << endl;

    out << str.toUpper() << endl;
    out << atr.toLower() << endl;

    return 0;
}

Nastavíme znakový proud na standardní výstup, tedy terminál/konzoli. Dále se objekt out chová velmi podobně jako knihovna iostream.

Dále vytvoříme QString proměnnou str a inicializujeme ji nějakým řetězcem: QString str = "líbí". Existuje vice možností inicializace, např. i QString str("líbí").

Protože jsem tvrdil, že tyto řetězce jsou modifikovatelné, je třeba to hned zkusit. Za původní obsah str přidáme nějaký další text pomocí a.append(" ITNetwork!!!") a abychom si předvedli sílu QString, tak zkusíme přidat i před něj nějaký text jako a.prepend("Mě se ");.

Výsledek pošleme na terminál a odřádkujeme: out << str << endl;

Další řádek bude chtít více slov. Chtěli jsme zjistit, kolik má řetězec znaků. Vhodná metoda k tomuto účelu je str.count(). Také lze použít i její ekvivalenty str.length() a str.size(), záleží na vašem zvyku a co si zapamatujete lépe.

Všimněte si, že výpis neobsahuje diakritiku a ani nemůže, protože běžný řetězec odeslaný na textový proud je vlastně ukazatel na pole typu char a nikoli QChar.

V posledních dvou řádcích pouze znaky řetězce převedeme na velká písmena pomocí out << str.toUpper() << endl a na malá jako out << atr.toLower() << endl. Zde bych chtěl upozornit, že tyto dvě metody nevrací změněný řetězec, nýbrž jeho změněnou kopii. Původní obsah se zachovává. A výsledek by měl vypadat nějak takto:

Konzolová aplikace
Mně se líbí ITNetwork!!!
Retezec obsahuje 23 znaku
MNĚ SE LÍBÍ ITNETWORK
mně se líbí itnetwork
Zavřete toto okno...

Jak bylo výše řečeno, texty se vnitřně ukládají jako pole QChar a tedy i takto lze k datům přistupovat. Zkusme si přidat tyto dva řádky, které vypíší znak na dané pozici (snad nemusím upozorňovat, že před return 0):

out << str[0] << endl;     // M
out << str[4] << endl;     // !

Samozřejmě, existuje i metoda str.at(n), kde n je pozice znaku v řetězci počítaná od 0 do str.size() - 1.

Argumenty řetězců

Dalším pozitivem QString je, že řetězce mohou mít argumenty. Chceme-li např. vypsat text: "Na louce se prohání 15 koní.", to by problém nebyl. Ale co když se jeden kůň zaběhne do lesa. To jich na louce máme již jen 14. Nechtělo by to nějakou variabilní možnost, jak jejich počet do řetězce vložit, než takto "na pevno"? Právě k tomu nám slouží metoda str.arg(var), která funguje podobně jako např. printf() v jazyce C.

Přidáme si další řádky na ukázku:

QString kone("Na louce se prohání %1 koňů.");
int louka = 15;
out << kone.arg(louka) << endl;

Další argumenty se mohou zřetězovat stejnou metodou arg(var1).arg(var2)...:

QString kone2("Na louce se prohání %1 koňý a %2 je (jsou) v lese.");
louka = 12;
int les = 3;
out << kone2.arg(louka).arg(les) << endl;

Výsledek na terminálu je podobný tomuto:

Konzolová aplikace
MNĚ SE LÍBÍ ITNETWORK
mně se líbí itnetwork
M
!
I
Na louce se prohání 15 koňů.
Na louce se prohání 12 koňý a 3 je (jsou) v lese.
Zavřete toto okno...

Pokud si přejete použít jiný typ argumentu než integer, není to problém. Dokonce na stejný řetězec můžete použít jakýkoli typ. Pravda, všechny jsem nikdy nezkoušel:

QString kone3("Kobyla má %1 hříbat a valach má %2 kobyl:-D.");
int hribe = 1;
double kobyla = 0.01;
out << kone3.arg(hribe).arg(kobyla) << endl;

A výsledek:

Konzolová aplikace
Na louce se prohání 15 koňů.
Na louce se prohání 12 koňý a 3 je (jsou) v lese.
Kobyla má 1 hříbat a valach má 0.01 kobyl:-D.
Zavřete toto okno...

Práce s podřetězci

Promažeme naše ukázky až na základ a popovídáme si o dalších metodách třídy QString, určitě se vám budou líbit - vybírají podřetězce. Když jsem kdysi začínal programovat na ZX Spectru, tak právě tyto funkce mi dost chyběly... i když je bylo možno nahradit jinou konstrukcí. Jsou to metody:

  • left(n) - vrátí n znaků zleva,
  • right(n) - vrátí n znaků zprava,
  • mid(n, m) - vrátí m znaků od pozice n.

Prosím, pamatujte, že pozice se číslují od nuly.

Zkusme si opět nějaký příklad:

#include <QTextStream>
#include <QString>

int main(void) {
    QString str("Jsme ve vesmíru jediní?");

    out << str.left(4) << endl;
    out << str.right(7) << endl;
    out << str.mid(8,7) << endl;

    return 0;

Výsledek:

Konzolová aplikace
Jsme
jediní?
vesmíru
Zavřete toto okno...

Zjištění druhu znaku

Dost často se může hodit rozhodnutí, zda znak v řetězci je písmeno, číslo, bílý znak anebo něco úplně jiného. Můžeme si zkusit udělat malou analýzu textu. Qt k tomu nabízí mnoho nástrojů. Např. již jen to, že celý text je iterovatelný v cyklu, že je k dispozici přístup k jednotlivým znakům a že pro znaky jsou metody, které umí rozhodnout jaký typ znaku to je. V základu se jedná o metody isLetter() pro písmena, isDigit() pro číslice, isSpace() pro bílé znaky a isPunct() pro interpunkci, tedy tečky, čárky, vykřičníky a tak podobně.

Myslím, že nejlepší bude si to hned vyzkoušet. Budeme analyzovat jednoduchou větu a zjišťovat počty různých druhů znaků:

#include <QTextStream>
#include <QString>          // Není nutné, ale zvyk je zvyk
#include <QChar>            // Jako o řádek výše
int main(void) {

    QTextStream out(stdout);

    int pismena  = 0;
    int cisla = 0;
    int mezery  = 0;
    int intpunk  = 0;

    QString str = "Na louce se pase 1 bílý\tkůň.\n";


    // Iterace nad řetězcem
    foreach(QChar s, str) {

        if (s.isDigit()) {
            cisla++;
        }
        else if (s.isLetter()) {
            pismena++;
        }
        else if (s.isSpace()) {
            mezery++;
        }
        else if (s.isPunct()) {
            intpunk++;
        }
    }

    // Výsledek vypíšeme
    out << QString("Máme celkem %1 znaků").arg(str.count()) << endl;
    out << QString("Z toho je %1 písmen").arg(pismena) << endl;
    out << QString("pak je tam %1 čísel").arg(cisla) << endl;
    out << QString("krom toho i %1 mezer").arg(mezery) << endl;
    out << QString("a %1 znaků interpunkce").arg(intpunk) << endl;

    return 0;
}

Myslím, že zde ani není nutné toho moc vysvětlovat. Snad jen onen cyklus foreach nad QString. Prostě každý znak je QChar, který se postupně ukládá do proměnné s, dokud se nenarazí na konec textu. Po spuštění by se mohla zobrazit podobná zpráva jako níže na obrázku:

Konzolová aplikace
Máme celkem 29 znaků
Z toho je 20 písmen
pak je tam 1 čísel
krom toho i 7 mezer
a 1 znaků interpunkce
Zavřete toto okno...

No aby toho nebylo naráz až přespříliš, tak dnešní povídání bych ukončil a příště ještě na toto téma navázal. Řetězce se používají velmi často a proto si něco řekneme o jejich změnách, porovnávání, doplňování a určitě si ještě na něco zajímavého vzpomenu.

V příští lekci, Jednoduchá kalkulačka v Qt a C++ - Layout, si navrhneme formulář pro kalkulačku.


 

Předchozí článek
Dokončení prvního okna v Qt a C++ - Tlačítko
Všechny články v sekci
Qt - Okenní/formulářové aplikace v C++
Přeskočit článek
(nedoporučujeme)
Jednoduchá kalkulačka v Qt a C++ - Layout
Článek pro vás napsal Virlupus
Avatar
Uživatelské hodnocení:
13 hlasů
Autor se věnuje webovým aplikacím, skladově-účetnímu softwaru, 3D grafice, lexiální analýze a parserování. Studuje fyziku na MFF UK. Učil IT na střední škole.
Aktivity