Hlavní navigace

GLib: Automatické doplňování řetězců

Michal Burda

Líbí se vám, jak třeba příkazový procesor "uhádne" konec příkazu poté, co napíšete pár prvních znaků a stisknete tabulátor? Chtěli byste podobnou vlastnost zakomponovat do svých programů? Proč vymýšlet již vymyšlené - používejte knihovnu GLib a její automatické doplňování řetězců!

Mechanizmus pro automatické doplňování řetězců stojí na datové struktuře GCompletion (viz níže), na kterou se volají všechny obslužné rutiny. K tomu, abyste mohli mechanizmus automatického doplňování řetězců používat, je nutné, abyste byli obeznámeni alespoň se základy práce s obousměrnými seznamy, o kterých se na Rootu psalo v minulých dvou dílech.

Doplňování řetězců funguje následovně. Speciálními funkcemi v  GCompletion u nejprve „zaregistrujete“ všechny možné (a smysluplné) řetězce. Voláním funkce g_completion_complete() si pak vyžádáte uhodnutí konce řetězce k zadanému „začátku“ (prefixu – řetězci, který je zadán jako argument). Funkce prohledá seznam všech řetězců, vyhodnotí, jak by asi měl pokračovat a vrátí seznam všech vhodných adeptů na doplnění a popřípadě i nový prefix.

Podívejme se nyní trochu blíže na onu strukturu GCompletion:

struct GCompletion
{
  GList* items;
  GCompletionFunc func;

  gchar* prefix;
  GList* cache;
};
items 
prefix 

Vytvoření nové struktury GCompletion

Abychom mechanizmus automatického doplňování řetězců mohli používat, je nutné nejprve vytvořit v paměti samotnou strukturu GCompletion. Provede se to příkazem

GCompletion* g_completion_new(GCompletionFunc func); 

Funkce požaduje jako argument konverzní funkci func a vrátí pointer na nový  GCompletion.

Funkce func je dána předpisem

gchar* (*GCompletionFunc)(gpointer); 

Očekává se od ní, že jako jediný argument převezme gpointer na data uložená v obousměrném seznamu a najde v nich řetězec, který předá jako svou návratovou hodnotu. Při takové práci musíte dbát na to, aby všechny řetězce, které funkce func zprostředkovává, byly neustále v paměti. Mechanizmus ve funkci g_completion_complete() to totiž pro správnou činnost vyžaduje. Nejsnáze se toho dosáhne tak, že v datech uložených v seznamu jsou přímo ony řetězce obsaženy a funkce func pak pouze k těmto datům přistupuje (viz příklad na konci kapitoly). To, že by se řetězce při každém volání funkce func teprve dynamicky tvořily a následně dealokovaly, je nepřípustné.

Budou-li v seznamu uloženy přímo řetězce, může být argument func funkce g_completion_new()   NULL ový.

Práce se seznamem „registrovaných“ položek

Se seznamem registrovaných položek (tedy s položkou items struktury GCompletion) se pracuje pomocí trojice následujících funkcí. Všechny tři požadují jako svůj první parametr cmp pointer na strukturu  GCompletion.

void g_completion_add_items(GCompletion *cmp, GList *items); 

…přidá mezi registrované prvky cmp->items GCompletion u cmp  prvky seznamu items. Předchozí výsledky doplňování řetězce se zruší – tedy dealokují a na NULL se nastaví cmp->prefixcmp->cache.

Přidávání prvků proběhne tak, že se vytvoří kopie uzlů GList seznamu do kterých se zkopírují pointery na jejich data. Vyplývá z toho tedy to, že seznam items musíte po použití sami dealokovat. Pozor však na data, které budou mezi oběma seznamy sdílené. Musíte zajistit, aby po celou dobu, kdy je mechanizmus doplňování řetězců může potřebovat, existovaly.

Potřebujete-li ze seznamu registrovaných prvků cmp->items naopak položky odstraňovat, použijte funkci

void g_completion_remove_items(GCompletion *cmp, GList
*items); 

Tato funkce prohledá seznam registrovaných prvků ( cmp->items) i seznam adeptů na doplnění řetězce ( cmp->cache) a odstraní z nich všechny prvky, jejichž data jsou v seznamu předaném jako parametr items. Odstraněním prvků se rozumí dealokace uzlů GList  – s uloženými daty se nebude dít nic.

A konečně poslední funkce, která je velmi jednoduchá:

void g_completion_clear_items(GCompletion *cmp); 

…jejím úkolem je odstranit všechny registrované položky a inicializovat GCompletion cmp, aby byl takový, jaký je po svém vytvoření – dealokují se celé seznamy cmp->items i cmp->cache  stejně tak jako prefix cmp->prefix. (Opět, aby bylo jasno: dealokací seznamu se rozumí vyprázdnění seznamu, tedy volání g_list_free()  – samotná data zůstanou v paměti nedotčena!)

Doplnění řetězce

Funkce

GList* g_completion_complete(GCompletion *cmp, gchar *prefix,
        gchar **new_prefix);

je klíčovou funkcí celého mechanizmu. Je to ona, kdo „odedře“ celou práci.

Jako první argument jí předejte pointer na GCompletion ( cmp) a jako druhý argument „začátek“ řetězce neboli prefix, ke kterému se bude provádět doplňování.

Funkce g_completion_complete() vrátí seznam všech prvků, jejichž řetězce začínají zadaným prefix em a do třetího argumentu ( new_prefix) uloží nejdelší možný „začátek“ řetězce, který je společný pro všechny prvky shodující se se zadaným prefix em nebo NULL, jestliže nenalezne jedinou položku, která by začínala prefix em.

Vrácený seznam je určen jen ke čtení a řetězec new_prefix by měl být po použití dealokován.

Dealokace GCompletion

void g_completion_free(GCompletion *cmp); 

…uvolní veškerou paměť použitou GCompletion em cmp včetně jeho seznamů. Datové položky seznamů však musíte dealokovat sami.

Příklad:

/* Priklad pouziti automatickeho doplnovani retezcu */

#include <glib.h>

/* Datova struktura (abychom to nemeli tak jednoduche) */
typedef struct {
  gchar *s;
  gint value;  /* Nejake dalsi polozky... */
} MyData;

/* Konverzni funkce */
gchar* konverze(gpointer data)
{
  return ((MyData *) data)->s;
}

/* Funkce pro vytvoreni dynamickych dat */
gpointer data_new(gchar* s, gint value)
{
  MyData* data;

  data = g_new(MyData, 1);
  data->s = s;
  data->value = value;
  return (gpointer) data;
}

/* Tisk */
void data_print(gpointer data, gpointer user_data)
{
  printf("%s - %d\n", ((MyData *) data)->s,
    ((MyData *) data)->value);
}

/* dealokace dat */
void data_dealloc(gpointer data, gpointer user_data)
{
  g_free(data);
}

/* Hlavni program */
gint main(void)
{
  GCompletion* cmp;
  GList* list = NULL;
  GList* completed;
  gchar* new_prefix;

  /* Vytvoreni noveho GCompletion */
  cmp = g_completion_new(konverze);

  /* Registrace nejakych polozek */
  list = g_list_append(list, data_new("dopis.txt", 2));
  list = g_list_append(list, data_new("dluznici.txt", 8));
  list = g_list_append(list, data_new("smlouva.txt", 13));
  list = g_list_append(list, data_new("doporuceni.txt", 1));
  list = g_list_append(list, data_new("dopis.gif", 6));
  g_completion_add_items(cmp, list);

  /* Doplneni retezce */
  completed = g_completion_complete(cmp, "k", &new_prefix);
  printf("Maximalni prefix: %s\n", new_prefix);
  g_list_foreach(completed, data_print, NULL);
  g_free(new_prefix);

  /* Doplneni retezce (podruhe) */
  completed = g_completion_complete(cmp, "do", &new_prefix);
  printf("\nMaximalni prefix: %s\n", new_prefix);
  g_list_foreach(completed, data_print, NULL);
  g_free(new_prefix);

  /* Doplneni retezce (potreti) */
  completed = g_completion_complete(cmp, "dopo", &new_prefix);
  printf("\nMaximalni prefix: %s\n", new_prefix);
  g_list_foreach(completed, data_print, NULL);
  g_free(new_prefix);

  /* Uvolneni pameti */
  g_completion_free(cmp);
  g_list_foreach(list, data_dealloc, NULL);
  g_list_free(list);

  return 0;
}

Spustíte-li si tuto ukázku, mělo by se vám vypsat:

Maximalni prefix: (null)

Maximalni prefix: dop
dopis.txt - 2
doporuceni.txt - 1
dopis.gif - 6

Maximalni prefix: doporuceni.txt
doporuceni.txt - 1

Co to znamená, si jistě pilný čtenář prostuduje sám.

Jakékoliv dotazy a připomínky prosím směřujte na mou adresu.

Našli jste v článku chybu?
Měšec.cz: Zdravotní a sociální pojištění 2017: Připlatíte

Zdravotní a sociální pojištění 2017: Připlatíte

DigiZone.cz: ČRo rozšiřuje DAB do Berouna

ČRo rozšiřuje DAB do Berouna

120na80.cz: 5 nejčastějších mýtů o kondomech

5 nejčastějších mýtů o kondomech

Vitalia.cz: Vychytané vály a válečky na vánoční cukroví

Vychytané vály a válečky na vánoční cukroví

Lupa.cz: Insolvenční řízení kvůli cookies? Vítejte v ČR

Insolvenční řízení kvůli cookies? Vítejte v ČR

Měšec.cz: Jak vymáhat výživné zadarmo?

Jak vymáhat výživné zadarmo?

Vitalia.cz: Proč vás každý zubař posílá na dentální hygienu

Proč vás každý zubař posílá na dentální hygienu

Vitalia.cz: Paštiky plné masa ho zatím neuživí

Paštiky plné masa ho zatím neuživí

Podnikatel.cz: Udávání a účtenková loterie, hloupá komedie

Udávání a účtenková loterie, hloupá komedie

Měšec.cz: Kdy vám stát dá na stěhování 50 000 Kč?

Kdy vám stát dá na stěhování 50 000 Kč?

Vitalia.cz: Spor o mortadelu: podle Lidlu falšovaná nebyla

Spor o mortadelu: podle Lidlu falšovaná nebyla

Lupa.cz: Proč firmy málo chrání data? Chovají se logicky

Proč firmy málo chrání data? Chovají se logicky

Podnikatel.cz: Chtějte údaje k dani z nemovitostí do mailu

Chtějte údaje k dani z nemovitostí do mailu

Vitalia.cz: Jsou čajové sáčky toxické?

Jsou čajové sáčky toxické?

Vitalia.cz: Mondelez stahuje rizikovou čokoládu Milka

Mondelez stahuje rizikovou čokoládu Milka

Vitalia.cz: Jmenuje se Janina a žije bez cukru

Jmenuje se Janina a žije bez cukru

120na80.cz: Co všechno ovlivňuje ženskou plodnost?

Co všechno ovlivňuje ženskou plodnost?

DigiZone.cz: Flix TV: dva set-top boxy za korunu

Flix TV: dva set-top boxy za korunu

Vitalia.cz: Potvrzeno: Pobyt v lese je skvělý na imunitu

Potvrzeno: Pobyt v lese je skvělý na imunitu

Měšec.cz: U levneELEKTRO.cz už reklamaci nevyřídíte

U levneELEKTRO.cz už reklamaci nevyřídíte