Prosíme přihlašte se nebo zaregistrujte.

Přihlašte se svým uživatelským jménem a heslem.
Vaše pomoc je stále potřeba!

Autor Téma: C: jak řešíte "složené funkce" (roury)?  (Přečteno 3943 krát)

daysleeper

  • Stálý člen
  • **
  • Příspěvků: 1206
C: jak řešíte "složené funkce" (roury)?
« kdy: 10 Března 2012, 23:58:45 »
Ahoj,

nějak se mi nedaří v C rozumně vyřešit problém postupného zpracování řetězce, a budu rád za pomoc.

Mějme řetězec, který chci např. nejdříve převést na upper case, a pak transliterovat, a pak třeba převést na html-friendly verzi. V bashi by to vypadalo pomocí rour nějak takto (zjednodušeně)
Kód: [Vybrat]
echo $rezetec | to_upper_case | translit | 2html
Jak to udělat rozumně v C? Prostě funkce
Kód: [Vybrat]
char *transfer (char *string) {
    return 2html ( translit ( upper_case (string)));
}

Jak ale vyřešit tu složenou funkci 2html ( translit ( upper_case (string)))? Důležité je, že výsledek může být větší než vstupní string. Zkoušel jsem to přes pointery, ale jakmile si každá funkce vytvoří svůj vlastní pointer, těžko se pak honí zpátky na free(). Pokud to řeším klasicky přes input_string, output_string, vzniká strašně nepřehledná směs, ve které je ještě nutno přesouvat řetězce sem tam
Kód: [Vybrat]
return  html ( translit ( upper_case (in, out, max_length1), out2, max_length2), out3, max_length3);
Díky za případné tipy nebo třeba odkazy na podobný zdrojový kód.

skunkos

  • Aktivní člen
  • *
  • Příspěvků: 399
    • Osobní web
Re:C: jak řešíte "složené funkce" (roury)?
« Odpověď #1 kdy: 11 Března 2012, 09:16:06 »
1. Můžeš využít třídu string z c++ http://www.cplusplus.com/reference/string/string/

2. A přes ukazatel na char (řetězec) by to mělo jít, není přece problem na naalokovat větší úsek paměti pro ten řetězec, ne? Stejně si budeš měnit konec řetězce přes '\0'.

3. Pokud chceš takhle "navlékat" to volání funkcí, tak musí pochopitelně každá brát stejně argumenty a následně stejné vracet.
ArchLinux (KDE)
Můj web

premet

  • Host
Re:C: jak řešíte "složené funkce" (roury)?
« Odpověď #2 kdy: 11 Března 2012, 10:17:56 »
Něco takového by mělo fungovat, v těch funkcích si pak můžeš vytvořit libovolný řetězec a samozřejmě těch funkcí může být n.
Kód: [Vybrat]
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

char * to_upper(char * input);
char * to_lower(char * input);

int main()
{
    char * text = (char*)malloc(50*sizeof(char));
    strcpy(text,"pokusny text");
   
    char * result = to_lower(to_upper(text));
   
    printf("%s\n",result);
   
    free(result);
   
    return 0;
}

char * to_upper(char * input)
{

    char * output_start;
    char * output = (char*)malloc(strlen(input)*sizeof(char));
   
    strcpy(output,input);
           
    free(input);

    output_start = output;
   
    while (*output) {

if (*output>= 97 && *output <= 122) {
             
     *output = *output - 32;

}
       
          output++;

     }

     return output_start;
}
   


char * to_lower(char * input)
{
    char * output_start;
    char * output = (char*)malloc(strlen(input)*sizeof(char));
   
    strcpy(output,input);
           
    free(input);

    output_start = output;
   
    while (*output) {

if (*output>= 65 && *output <= 90) {
             
     *output = *output + 32;

}
       
          output++;

     }

     return output_start;
   
}
« Poslední změna: 11 Března 2012, 10:20:19 od Honza Grulich »

Martin - ViPEr*CZ*

  • Závislák
  • ***
  • Příspěvků: 3047
Re:C: jak řešíte "složené funkce" (roury)?
« Odpověď #3 kdy: 11 Března 2012, 10:31:54 »
Proč tam děláš todle?

Kód: [Vybrat]
output_start = output;
Tam jenom do další proměnný uložíš ukazalet na output, ale to už je zbytečný ne? Stačí na konci vrátit

Kód: [Vybrat]
return output;
a tu konstrukci vynechat, protože podle mě je k ničemu.
Použil bych možná http://www.cplusplus.com/reference/clibrary/cctype/tolower/ a http://www.cplusplus.com/reference/clibrary/cctype/toupper/ pro změnu velikosti písmen, aby to bylo ještě o něco málo přehlednější.

A není takto náhodou stejně velký řetězec i na výstupu jak jsi to zapsal?
« Poslední změna: 11 Března 2012, 10:39:12 od Martin - ViPEr*CZ* »
Open source is gold way... Mint 17.2, Debian 8.1 Jessie| Ubuntu Wiki (návody) | Google vyhledávač | Qt4 návody

premet

  • Host
Re:C: jak řešíte "složené funkce" (roury)?
« Odpověď #4 kdy: 11 Března 2012, 12:10:26 »
To si ukládám ukazatel na začátek řetězce, protože si ten pointer v cyklu posouvám a vracel bych ukazatel na konec řetězce.
Jo v tomhle příkladu je ten řetezec stejně velký, ale je to jen příklad, klidně si můžeš alokovat jak velký chceš a udělat s ním co chceš.

např
Kód: [Vybrat]
char * to_upper(char * input)
{

    char * output_start;
    char * output = (char*)malloc(2*strlen(input)*sizeof(char));
   
    strcpy(output,input);
           
    free(input);

    output_start = output;
   
    while (*output) {

if (*output>= 97 && *output <= 122) {
             
     *output = *output - 32;

}
       
          output++;

     }
     
     strcpy(output,"dodatek");
     
     return output_start;
}
« Poslední změna: 11 Března 2012, 12:14:26 od Honza Grulich »

Martin - ViPEr*CZ*

  • Závislák
  • ***
  • Příspěvků: 3047
Re:C: jak řešíte "složené funkce" (roury)?
« Odpověď #5 kdy: 11 Března 2012, 13:58:36 »
Ah jsem to překoukl.... máš pravdu... ty se posunuješ po znakách a na konci zůstaneš na konci... v tomhle případě bych to šlo určitě napsat přehlednějc.
Jestli dotyčný potřebuje funcki co přijme pole a po zpracování pole může mít jinou velikost než původní, pak samozřejmě je potřeba použít kopírování obecně, přesně jak naznačuje Honza. A jinak samozřejmě záleží k čemu ta dotyčná funkce slouží.... například na změnu velikosti znaků bych použil to co jsem psal výše.

Btw. při vytváření nové proměnné
Kód: [Vybrat]
char * output = (char*)malloc(strlen(input)*sizeof(char));toto padá a je potřeba tam (jestli se nepletu) započítat ještě nulovej znak na konci... tj. +1 pro malloc (otestováno v rychlosti na VS 2005).
Open source is gold way... Mint 17.2, Debian 8.1 Jessie| Ubuntu Wiki (návody) | Google vyhledávač | Qt4 návody

daysleeper

  • Stálý člen
  • **
  • Příspěvků: 1206
Re:C: jak řešíte "složené funkce" (roury)?
« Odpověď #6 kdy: 11 Března 2012, 14:15:09 »
@Honza Grulich:
děkuji za ukázku kódu, takhle to asi bude nejlepší. Jako začátečník mám hlavně problém s tím, že v komplikovanějším kódu vzniká hodně pointerů a zapomínám je uvolňovat.

Ještě jednu nevýhodu to má: nelze jednoduše použít takovou funkci na dynamicky i staticky alokovanou proměnnou (jako třeba strlen - tam je jedno, jakou proměnnou mu podsunu jako input)
Kód: [Vybrat]
int main()
{
    char * text = (char*)malloc(50*sizeof(char));
    char text_static [ 256 ];

    strcpy(text,"pokusny text");
    strcpy(text_static, "pokusny2");
   
    // toto je OK   
    char * result = to_lower(to_upper(text));
   
    // tady samozrejme dojde k chybe free(): invalid pointer:
    char * r2 = to_lower(to_upper(text_static));

    printf("%s\n",result);
   
    free(result);
   
    return 0;
}
Napadá mě jedině použít nějaký wrapper, který by vše převedl na dynamiku, a pak to teprve předhodil další funkci, ale je to trochu ble řešení.

@Martin - ViPEr*CZ*:
Jj, má tam chybu, mělo by být +1  ;)
Kód: [Vybrat]
char * output = (char*)malloc(2*strlen(input)*sizeof(char) + 1);

@skunkos:
C++ zatím neumím  :P

Martin - ViPEr*CZ*

  • Závislák
  • ***
  • Příspěvků: 3047
Re:C: jak řešíte "složené funkce" (roury)?
« Odpověď #7 kdy: 11 Března 2012, 14:26:15 »
@daysleeper Ještě jednu nevýhodu to má: nelze jednoduše použít takovou funkci na dynamicky i staticky alokovanou proměnnou
Jak to? Samozřejmě musíš vypustit z těl free(...); a přepsat to trochu. Dokonce tím, že ti tam vstupuje ukazatel a nic se nekopíruje, pak můžeš vypustit i return a rovnou ti to upraví proměnnou, kterou těm funkcím předáš.

A tady jde toto přepsat:

Kód: [Vybrat]
char * output_start;
char * output = (char*)malloc((2*strlen(input)*sizeof(char))+10);
   
strcpy(output,input);
           
free(input);

input = (char*)malloc(2*strlen(output)*sizeof(char));
strcpy(input, output);

free(output);

... //a teď je input o 9 znaků větší než byla

strcat(input, "********9");


Hmmm taková metoda risize alokované proměnné jak vyšitá  ;) Todle by mělo rozšířit input a ještě na konec nakopírovat 9 znaků.
« Poslední změna: 11 Března 2012, 14:28:01 od Martin - ViPEr*CZ* »
Open source is gold way... Mint 17.2, Debian 8.1 Jessie| Ubuntu Wiki (návody) | Google vyhledávač | Qt4 návody

premet

  • Host
Re:C: jak řešíte "složené funkce" (roury)?
« Odpověď #8 kdy: 11 Března 2012, 14:53:48 »
Jo vždycky zapomenu, že strlen vrací délku bez '\0' a jelikož mi to nespadlo tak sem si to neuvědomil  :)

@Martin
Jestli chápu tenhle tvůj příklad, tak ty modifikuješ vstupní řetězec a nic pak vracet nebudeš, pak by ale nešlo funkce1(funkce2(funkce3(string))), musel by si to volat po jednom funkce3(string), funkce2(string), funkce1(string).

PS: Na resize můžeš použít realloc, je to jednodušší  :)
« Poslední změna: 11 Března 2012, 14:55:37 od Honza Grulich »

Martin - ViPEr*CZ*

  • Závislák
  • ***
  • Příspěvků: 3047
Re:C: jak řešíte "složené funkce" (roury)?
« Odpověď #9 kdy: 11 Března 2012, 17:37:13 »
No je hezky vidět jak zapomínáme každej na něco :)

@Honza: Chápeš ho naprosto přesně. Otázka je co bude přehlednější v kódu pro návrh? To moje děsně zpřehlední onu samotnou funkci. Navíc pak na třech řádcích je patrné na první pohled životní cyklus onoho stringu a podle mě nezapomeneš na závorky. V tom řetězení tří a více funkcí do jednoho řádku člověk lehce zabředne do závorek a začátečník se pak může ztratit v posloupnosti vykonávání jednotlivých funkcí.
Je to můj názor na danou situaci. Samozřejmě proti vracení v returnu modifikovaného řetězce také nic nemám, ale měl by se vracet vstupní parametr jinak bych ho definoval jako const, aby bylo hned patrné, že v návratu se mi vrací jiný blok paměti (řetězec) než jsem funkci předal.

PS: děkuji za doplnění realloc... jsem zvyklý na new-delete operátory a tydle srandy ze samotného céčka spíše opomínám  :-[
Open source is gold way... Mint 17.2, Debian 8.1 Jessie| Ubuntu Wiki (návody) | Google vyhledávač | Qt4 návody

premet

  • Host
Re:C: jak řešíte "složené funkce" (roury)?
« Odpověď #10 kdy: 11 Března 2012, 18:35:25 »
@Martin:
Asi to bude přehlednější, záleží jestli to přímo takhle Daysleeper nechce mít nebo nemá nějaký zadání odněkud, pak by neměl navybranou. Já v Cčku taky jinak nedělám, krom pár projektů do školy z dřívějška, navíc nejsem moc zvyklej používat ani delete operátor, když v Qt při vytváření dávám parametr rodiče a pak se nemusím starat již o uvolnění  :)

Martin - ViPEr*CZ*

  • Závislák
  • ***
  • Příspěvků: 3047
Re:C: jak řešíte "složené funkce" (roury)?
« Odpověď #11 kdy: 11 Března 2012, 18:51:23 »
Citace
dávám parametr rodiče
Co tím myslíš?
Open source is gold way... Mint 17.2, Debian 8.1 Jessie| Ubuntu Wiki (návody) | Google vyhledávač | Qt4 návody

daysleeper

  • Stálý člen
  • **
  • Příspěvků: 1206
Re:C: jak řešíte "složené funkce" (roury)?
« Odpověď #12 kdy: 11 Března 2012, 19:14:54 »
Zadání odněkud nemám, učím se C (píšu si vlastního "email klienta") a zkoumám možnosti. Tímto řeším případy, kdy je potřeba vzít string (tělo mailu, header) třeba v base64 nebo QUOTED, dekódovat, převést na UTF-8, a pak případně ještě doplnit o html, aby se to dalo hezky zobrazit v prohlížeči. 

Postupné zadávaní funkcí namísto funkce složené by bylo možné, ale má pro mě dvě nevýhody:
 - řetězec se kopíruje "zbytečně" 2x (in → out; out → in). Není to tak hrozné, ale příloha může mít třeba 10MB.
 - nejde psát takové (pro mě krásné) funkce typu
Kód: [Vybrat]
char *all2utf8 (char *coded, enum charsets charset, enum encodings encoding) {
 
  if (! (coded && there_is_st_here(coded))) return NULL;
  switch (charset) {
    case US_ASCII:        //the same for ascii & iso-8859-[12]
    case ISO_8859_2:
    case ISO_8859_1:
      switch (encoding) {
        case QUOTED: return iso8859_to_utf8(quoted2bin(coded), charset);
        case BASE64: return iso8859_to_utf8(decode64(coded), charset);
        case BYTE: return iso8859_to_utf8(coded, charset);
        default: break;
      }
      break;

    // ....
kde prostě jen použiju stručné return _něco_.

Martin - ViPEr*CZ*

  • Závislák
  • ***
  • Příspěvků: 3047
Re:C: jak řešíte "složené funkce" (roury)?
« Odpověď #13 kdy: 11 Března 2012, 19:25:48 »
@daysleeper: no právě přes ten ukazatel jak jsme psali výše se nic nekopíruje... pracuje se stále s jedním řetězcem (blokem paměti).... samozřejmě to byl jen návrh jak to mít přehlednější... a i takto by to šlo napsat bez return a mít to takto:

Kód: [Vybrat]
case BYTE:
   decode64(coded);
   iso8859_to_utf8(code, charset);
   break;

A navíc ještě pro všechny case je funkce iso8859_to_utf8(code, charset); společná a mohl by jsi ji tím pádem mít jen jednou před tím break uvedeným v příkladu...takže ve skutečnosti téměř stejný počet řádků (až na breaky jednotlivých case encodingů)... je na zvážení jestli je to pak přehlednější... ono ostatně v C tě netrápí dědičnost návrhu... tak máš i na výběr... pokud by to byl nějaký objektový návrh, pak jedno či druhé řešení by se nemuselo vyplatit.
« Poslední změna: 11 Března 2012, 19:31:45 od Martin - ViPEr*CZ* »
Open source is gold way... Mint 17.2, Debian 8.1 Jessie| Ubuntu Wiki (návody) | Google vyhledávač | Qt4 návody

premet

  • Host
Re:C: jak řešíte "složené funkce" (roury)?
« Odpověď #14 kdy: 11 Března 2012, 20:10:53 »
Citace
dávám parametr rodiče
Co tím myslíš?
Citace
QObjects organize themselves in object trees. When you create a QObject with another object as parent, the object will automatically add itself to the parent's children() list. The parent takes ownership of the object; i.e., it will automatically delete its children in its destructor.

daysleeper

  • Stálý člen
  • **
  • Příspěvků: 1206
Re:C: jak řešíte "složené funkce" (roury)?
« Odpověď #15 kdy: 11 Března 2012, 20:12:01 »
...
A navíc ještě pro všechny case je funkce iso8859_to_utf8(code, charset); společná a mohl by jsi ji tím pádem mít jen jednou před tím break uvedeným v příkladu......
To je fakt... Ještě to promyslím... Díky.

Martin - ViPEr*CZ*

  • Závislák
  • ***
  • Příspěvků: 3047
Re:C: jak řešíte "složené funkce" (roury)?
« Odpověď #16 kdy: 12 Března 2012, 11:17:37 »
Citace
dávám parametr rodiče
Co tím myslíš?
Citace
QObjects organize themselves in object trees. When you create a QObject with another object as parent, the object will automatically add itself to the parent's children() list. The parent takes ownership of the object; i.e., it will automatically delete its children in its destructor.
Přemýšlím jak to je dělané... můžeš mi poslat nějakej sample inicializace. Furt totiž přemýšlím, že musíš mít nějakej kontejner a nad ním stejně zavolat destruktor a on potom vymaže svoje děti taky přes destruktory, ale furt musíš inicializovat destrukci toho rodiče a na tu nesmíš zapomenout.
Open source is gold way... Mint 17.2, Debian 8.1 Jessie| Ubuntu Wiki (návody) | Google vyhledávač | Qt4 návody

 

Provoz zaštiťuje spolek OpenAlt.