Spojový seznam v C - zápis do souboru

Jenda

Re:Spojový seznam v C - zápis do souboru
« Odpověď #15 kdy: 22. 10. 2016, 10:12:23 »
No, tu fci s více parametry zase zrušim a pro každý parametr si napíšu prasácky vlastní fci.

Jenže to bude mít jinou sémantiku. Místo toho, aby měla jedna položka k sobě přiřazené atributy, budeš mít ve spojáku náhodný mix položek a různých atributů.

Kód: [Vybrat]
typedef struct katalog {
  char nazev [50];
Doporučil bych spíš paměť alokovat dynamicky podle délky toho řetězce, tj. mít tam char*. Zase teda záleží na užití.

Jakmile název bude obsahovat mezeru, už to asi nepůjde ukládat a načítat tak triviálně přes fprintf/scanf. Asi bych si napsal vlastní ukládání a načítání řetězců, a to buď jako délka+obsah, nebo null-terminated. A pokud bych to chtěl mít do budoucna rozšiřitelné a kompatibilní se zbytkem světa, vykašlal bych se na nějakou vlastní serializaci a použil třeba Procotol Buffers. Pro tvůj případ stačí opsat example z dokumentace.


Jenda

Re:Spojový seznam v C - zápis do souboru
« Odpověď #16 kdy: 22. 10. 2016, 10:15:37 »
A pokud fakt mocí mermo potřebuješ variabilní množství argumentů (v tomto případě bych ale raději použil parametr, který bude obsahovat bitmapu nastavovaných parametrů), tak se podívej na stdarg(3).

Thomas

Re:Spojový seznam v C - zápis do souboru
« Odpověď #17 kdy: 22. 10. 2016, 11:42:59 »
A pokud fakt mocí mermo potřebuješ variabilní množství argumentů (v tomto případě bych ale raději použil parametr, který bude obsahovat bitmapu nastavovaných parametrů), tak se podívej na stdarg(3).

No, já na ten projekt koukal do 3 ráno, takže to zjednodušim na přidání 2 položek.
S tím by snad problém být nemusel. Ono je tam dost striktní zadání u té semestrálky a já C neovládám v takové  míře, abych v něm dělal složitější úpravy. Kdysi jsem ho měl kvuli PIC a projel jsem si celého Herouta

Kit

Re:Spojový seznam v C - zápis do souboru
« Odpověď #18 kdy: 22. 10. 2016, 12:59:39 »
Asi bych začal výběrem serializačního formátu pro ukládání dat. V daném případě by mohl vyhovovat formát CSV.

Thomas

Re:Spojový seznam v C - zápis do souboru
« Odpověď #19 kdy: 22. 10. 2016, 19:37:46 »
Tak už se mi konečně podařilo oživit základy C, přidávám parametry, ale mam problém s přidáním názvu, ona to určitě bude zase nějaká blbost, která mi uniká :-/
Tady je zdroják headeru:

Kód: [Vybrat]
#ifndef PARAMETRY_H
#define PARAMETRY_H

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <malloc.h>
#include <string.h>
#define HODNOTA 100



typedef struct katalog {

             
    int radial_rychlost;
    int jasnost; 
    int hmotnost;
    char nazev[HODNOTA];
   
    struct katalog *nasledujici;

} KATALOG;


void vloz_parametr(KATALOG **pps, int parametr_1, int parametr_2, int parametr_3, char *parametr_4) {
   
    KATALOG *ps;
   
    ps = (KATALOG *) malloc(sizeof(KATALOG));
   
    if(ps == NULL) {
        fputs("Chybna alokace pameti!\n", stderr);
       
        return;
    }
   
   
   ps->hmotnost = parametr_1;
   ps->nasledujici;
   ps->jasnost = parametr_2;
   ps->nasledujici;
   ps->radial_rychlost = parametr_3;
   ps->nasledujici;
   ps->nazev = parametr_4;
   
   
   
   
    *pps = ps;   
   
};


#endif

A tady main:

Kód: [Vybrat]
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include "parametry.h"
#include "mazani.h"
#include "hledani.h"
#define ROZSAH 100

void vypis_katalog(const KATALOG *ps);
void vloz_parametr(KATALOG **pps, int parametr_1, int parametr_2, int parametr_3, char *parametr_4);

void vypis_katalog(const KATALOG *ps) {
   
    if(!ps)
        return;
   
    printf("Hmotnost: %i\n", ps->hmotnost);
    vypis_katalog(ps->nasledujici);
    printf("Radialni rychlost: %i\n", ps->radial_rychlost);
    vypis_katalog(ps->nasledujici);
    printf("Jasnost: %i\n", ps->jasnost);
    vypis_katalog(ps->nasledujici);
    printf("Nazev: %c\n", ps->nazev);
    vypis_katalog(ps->nasledujici);
   
};



int main() {
   
   
   
    FILE *fa;
   
    fa = fopen("/home/teodor/Plocha/parametry_exoplanet.txt", "w");
   
    if(fa == NULL) {
       
        fputs("Nelze otevrit soubor!\n", stderr);
       
        return 1;
       
    } else if(fa != NULL) {
       
        printf("Soubor uspesne otevren!\n");
       
    }
             
   
   
    int a, b, c;
    char jmeno[ROZSAH];
   
    printf("Zadejte radialni rychlost: \n");
    scanf("%d", &a);
    printf("Zadejte jasnost: \n");
    scanf("%d", &b);
    printf("Zadejte hmotnost: \n");
    scanf("%d", &c);
    printf("Zadejte nazev: \n");
    scanf("%c", &jmeno);
   
        KATALOG *s;
       
        s = NULL;   
       
        vloz_parametr(&s, a, b, c);
    //    fprintf(fa, "%d%d%d\n", a, b, c);       
       
        vypis_katalog(s);
       
       
       
   
   
   
    fclose(fa);
     
   
    return 0;
       
}

Bude mi stačit nějaký nakopnutí, u názvu nechci mít dynamicky přidělenou paměť. Název nebude obsahovat mezery, jen jedno slovo.


nou

Re:Spojový seznam v C - zápis do souboru
« Odpověď #20 kdy: 22. 10. 2016, 22:40:51 »
Treba pouzit
Kód: [Vybrat]
strcpy(ps->nazev, parametr_4);

Thomas

Re:Spojový seznam v C - zápis do souboru
« Odpověď #21 kdy: 22. 10. 2016, 23:08:07 »
Treba pouzit
Kód: [Vybrat]
strcpy(ps->nazev, parametr_4);


Díky, já už jsem opravdu přepracovaný; dva dny po sobě jsem šel spát ve tři ráno a unikaji mi tyhle maličkosti.

Re:Spojový seznam v C - zápis do souboru
« Odpověď #22 kdy: 22. 10. 2016, 23:57:29 »
Okej, patnáctiletej beďatej tydýt se právě vyřádil. :D

Pevně doufám, že to budeš brát jenom jako návrh řešení a ne jako hotovou věc. Byl bych rád, kdyby sis všiml některých kontrol na selhání funkcí (např. scanf).

Soubor jsem změnil na /tmp/soubor.txt, kdybys jej hledal.
Kód: (parametry.h) [Vybrat]
/*
 * Projekt Bla
 *
 * Datové struktury katalogu.
 */

#ifndef PARAMETRY_H
#define PARAMETRY_H

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <malloc.h>
#include <string.h>



///////////////
// STRUKTURY //
///////////////

/// \brief položka seznamu
typedef struct katalog {
    int radial_rychlost;
    int jasnost;
    int hmotnost;
    char *nazev;
   
    struct katalog *nasledujici;
} KATALOG;



//////////////////////
// PROTOTYPY FUNKCÍ //
//////////////////////


/// \brief Vytvoří položku spojového seznamu.
///
/// \param p_p_clanek    Ukazatel k ukazateli článku.
/// \param rychlost      Rychlost položky.
/// \param jas           Jas položky.
/// \param hmotnost      Hmotnost položky.
/// \param nazev         Název položky.
/// \return              EXIT_SUCCESS při úspěchu, EXIT_FAILURE při selhání.
int nova_polozka(KATALOG **p_p_clanek, int rychlost, int jas, int hmotnost, char *nazev);

/// \brief Smaže položku spojového seznamu.
///
/// POZOR: Na položku již nesmí být ukazováno.
///
/// \param p_clanek      Článek ke smazání.
/// \return              EXIT_SUCCESS při úspěchu, EXIT_FAILURE při selhání.
int smaz_polozku(KATALOG *p_clanek);

/// \brief Smaže katalog (spojový seznam).
///
/// \param p_clanek      První článek seznamu.
/// \return              EXIT_SUCCESS při úspěchu, EXIT_FAILURE při selhání.
int smaz_katalog(KATALOG *p_clanek);

/// \brief Vypíše na standardní výstup informace o položce.
///
/// \param p_clanek      Ukazatel na položku.
void vypis_polozku(const KATALOG *p_clanek);

/// \brief Vypíše všechny položky z katalogu.
///
/// \param p_polozka     Ukazatel na první položku.
void vypis_katalog(const KATALOG *p_clanek);


/////////////////////////
// IMPLEMENTACE FUNKCÍ //
/////////////////////////

int nova_polozka(KATALOG **p_p_clanek, int rychlost, int jas, int hmotnost, char *nazev) {
   
    // -- deklarace -- //
    KATALOG *p_clanek;
    size_t nazev_delka;
    char *nazev_kopie;
   
    // -- kontrola argumentů -- //
   
    if (p_p_clanek == NULL) {
        fputs("CHYBA V PROGRAMU: nova_polozka() zavolána s NULL-ovým cílovým umístěním.\n", stderr);
        return EXIT_FAILURE;
    }
   
    if (nazev == NULL) {
        fputs("CHYBA V PROGRAMU: nova_polozka() zavolána s NULL-ovým názvem.\n", stderr);
        return EXIT_FAILURE;
    }
   
    // -- zkopírování názvu -- //
   
    // zjištění délky původního názvu ve znacích
    nazev_delka = strlen(nazev);
   
    // alokace kopie o délce počet znaků + null terminator
    nazev_kopie = malloc(nazev_delka + 1);
   
    // kontrola alokace
    if (nazev_kopie == NULL) {
        fputs("Chyba: nemohu alokovat pamet pro nazev polozky.\n", stderr);
       
        return EXIT_FAILURE;
    }
   
    // samotné kopírování
    strncpy(nazev_kopie, nazev, nazev_delka);
   
    // kontrola na null terminator
    nazev_kopie[nazev_delka] = '\0';
   
    // -- vytvoření položky -- //
   
    // alokace položky
    p_clanek = (KATALOG *) malloc(sizeof(KATALOG));
   
    // kontrola alokace
    if(p_clanek == NULL) {
        fputs("Chybna nemohu alokovat pamet pro datovou polozku.\n", stderr);
       
        return EXIT_FAILURE;
    }
    // kopírování dat
    p_clanek->radial_rychlost = rychlost;
    p_clanek->jasnost         = jas;
    p_clanek->hmotnost        = hmotnost;
    p_clanek->nazev           = nazev_kopie;
    p_clanek->nasledujici     = NULL;
    // navrácení ukazatele na novou položku
    *p_p_clanek = p_clanek;   
   
    return EXIT_SUCCESS;
};


int smaz_polozku(KATALOG *p_clanek) {
   
    // -- kontrola argumentu -- //
    if (p_clanek == NULL) {
        fputs("CHYBA V PROGRAMU: smaz_polozku() zavolána s NULL-ovým pointerem.\n", stderr);
       
        return EXIT_FAILURE;
    }
   
    // smazání kopie názvu
    if (p_clanek->nazev != NULL)
        free(p_clanek->nazev);
   
    // smazání položky
    free(p_clanek);
   
    return EXIT_SUCCESS;
}

int smaz_katalog(KATALOG *p_katalog) {
    KATALOG *p_clanek = p_katalog;
   
    while (p_clanek != NULL) {
        KATALOG *p_dalsi = p_clanek->nasledujici;
       
        if (!smaz_polozku(p_clanek))
            return EXIT_FAILURE;
       
        p_clanek = p_dalsi;
    }
}

void vypis_polozku(const KATALOG *p_clanek) {
   
    // kontrola argumentů
    if(p_clanek == NULL)
        return;
   
    // výpis na standardní výstup
    printf("\"%s\":\n", p_clanek->nazev);
    printf(" * Hmotnost:          %d\n", p_clanek->hmotnost);
    printf(" * Jasnost:           %d\n", p_clanek->jasnost);
    printf(" * Radialni rychlost: %d\n", p_clanek->radial_rychlost);
    printf("\n");
   
};

void vypis_katalog(const KATALOG *p_katalog) {
    const KATALOG *p_clanek = p_katalog;
   
    int n = 1;
    while (p_clanek != NULL) {
   
        printf("%d. ", n++);
        vypis_polozku(p_clanek);
       
        p_clanek = p_clanek->nasledujici;
    }
}

#endif
Kód: (main.c) [Vybrat]
/*
 * Projekt Bla
 *
 * Hlavní část programu.
 */
#include <stdlib.h>
#include <unistd.h>
#include <errno.h>
#include "parametry.h"

// maximální délka názvu
#define INPUT_BUFFER 256

//////////////////////
// PROTOTYPY FUNKCÍ //
//////////////////////

/// \brief Vstupní bod programu.
///
/// \return     0 při úspěchu, 1 při chybě.
int main(void);

/// \brief Přečte od uživatele číselnou hodnotu.
///
/// \param vyzva    Výzva k zadání údaje.
/// \return         Přečtené číslo.
int ziskej_cislo(const char *vyzva);

/// \brief Přečte od uživatele text.
///
/// \param vyzva     Výzva k zadání údaje.
/// \param buffer    Buffer pro uložení textu.
/// \param max       Maximální počet přečtených znaků.
void ziskej_text(const char *vyzva, char *buffer, size_t max);

/// \brief Přečte všechny dostupné znaky ze standardního vstupu a zahodí je.
void vycisti_vstup(void);

/// \brief Vypíše exoplanety do souboru.
///
/// \param soubor       Soubor, do kterého zapisovat.
/// \param p_katalog    Katalog k vypsání.
/// \return             EXIT_SUCCESS při úspěchu, EXIT_FAILURE při selhání.
void vypis_soubor(FILE *soubor, KATALOG *p_katalog);

/////////////////////////
// IMPLEMENTACE FUNKCÍ //
/////////////////////////


void vycisti_vstup(void) {
    char ch;
       
    do {
        ch = getchar();
    } while (ch != '\n' && ch != EOF);
}


int ziskej_cislo(const char *vyzva) {
    int hodnota;
   
    // opakuj, dokud nezískáš platnou hodnotu
    while (1) {
        int precteno;
       
        if (vyzva != NULL) {
            fputs(vyzva, stdout);
            fflush(stdout);
        }
       
        precteno = scanf(" %d", &hodnota);
       
        vycisti_vstup();
        if (precteno == 1) {
            break;
        }
    }
   
    return hodnota;
}

void ziskej_text(const char *vyzva, char *buffer, size_t max) {
    // stackoverflow: http://stackoverflow.com/a/4023921/1561345
    size_t delka;
   
    // opakuj, dokud nezískáš neprázdnou hodnotu
    do {       
        if (vyzva != NULL) {
            fputs(vyzva, stdout);
            fflush(stdout);
        }
    } while (fgets(buffer, max, stdin) == NULL);
   
    delka = strlen(buffer);
   
    // pokud uživatel zadal příliš dlouhé jméno, je třeba odhodit zbytek v zásobě.
    if (buffer[delka-1] != '\n') {
        vycisti_vstup();
        return;
    }

    // jinak jen ořízneme nový řádek.
    buffer[delka-1] = '\0';
    return;
}

void vypis_soubor(FILE *soubor, KATALOG *p_katalog) {
    KATALOG *p_clanek = p_katalog;
    while (p_clanek != NULL) {
        fprintf(soubor, "%s,%d,%d,%d\n",
                          p_clanek->nazev,
                          p_clanek->hmotnost,
                          p_clanek->jasnost,
                          p_clanek->radial_rychlost);
        p_clanek = p_clanek->nasledujici;
    }
}

int main(void) {
    FILE *fa = NULL;
    KATALOG *p_katalog = NULL, *p_clanek_posledni = NULL;
    int n = 1;
    char vstup[INPUT_BUFFER];
   
    // -- otevření souboru -- //
    fa = fopen("/tmp/soubor.txt", "w");
   
    if (fa == NULL) {
        fprintf(stderr, "Nepodarilo se otevrit soubor: %s!\n", strerror(errno));
       
        return EXIT_FAILURE;
       
    }
    printf("Soubor uspesne otevren!\n");
   
   
    // -- čtení exoplanet ze standardního vstupu -- //
    while(1) {
        int rychlost, jasnost, hmotnost;
        KATALOG *p_polozka = NULL;
       
        printf("Exoplaneta cislo %i\n", n++);
        ziskej_text("Zadejte nazev: \n", vstup, INPUT_BUFFER);
        hmotnost = ziskej_cislo("Zadejte hmotnost: \n");
        jasnost  = ziskej_cislo("Zadejte jasnost: \n");
        rychlost = ziskej_cislo("Zadejte radialni rychlost: \n");
       
        if (nova_polozka(&p_polozka, rychlost, jasnost, hmotnost, vstup) == EXIT_FAILURE)
            return EXIT_FAILURE;
       
        // aktualizace ukazatelů
        if (p_katalog == NULL) {
            p_katalog = p_polozka;
        }
        if (p_clanek_posledni != NULL) {
            p_clanek_posledni->nasledujici = p_polozka;
        }
        p_clanek_posledni = p_polozka;
       
        // dotaz na pokračování
        ziskej_text("Prejete si pridat dalsi exoplanety? [Y/n] ", vstup, INPUT_BUFFER);
        if (strcmp(vstup, "n") == 0 || strcmp(vstup, "N") == 0)
            break;
       
    }
   
    // -- výpis exoplanet na standardní výstup -- //
   
    fputs("\nExoplanety:\n\n", stdout);
    vypis_katalog(p_katalog);

    // -- výpis exoplanet do souboru -- //
   
    vypis_soubor(fa, p_katalog);

    // -- smazání seznamu -- //
   
    if (!smaz_katalog(p_katalog)) {
        return EXIT_FAILURE;
    }
   
    // -- uzavření souboru -- //
   
    if (fclose(fa) == EOF) {
        printf("Soubor se nepodarilo uzavrit: %s.\n", strerror(errno));
        return EXIT_FAILURE;
    }
   
    return EXIT_SUCCESS;
       
}

Re:Spojový seznam v C - zápis do souboru
« Odpověď #23 kdy: 23. 10. 2016, 00:14:20 »
Citace
operace přidání, mazání, hledání
  • Přídání:
    • na konec: získáš poslední položku seznamu a ukazatel nasledujici nastavíš na pointer na novou položku.
    • doprostřed:
Výchozí stav je toto:
Kód: [Vybrat]
[0]->[2]Pak vytvoříš položku a jako její nasledujici nastavíš budoucí následující položku ([2]):
Kód: [Vybrat]
[0]->[2]
[1]--^
Pak upravíš [0], aby ukazovala na [1]:
Kód: [Vybrat]
[0]->[1]->[2]
  • Mazání:
Výchozí pseudostav:
Kód: [Vybrat]
[0]->[1]->[2]Pak v [0] nastavíš ukazatel nasledujici na [2]:
Kód: [Vybrat]
[0]->[2]
[1]--^
Pak smažeš [1].
Něco zajímavého: https://en.wikipedia.org/wiki/Read-copy-update.
  • Hledání: Uchováváš ukazatel na aktuální položku. Tu zkontroluješ, jestli odpovídá. Když ne, tak jako aktuální nastavíš nasledujici položku. Když dojdeš k NULL, prošel jsi celý seznam.

Teo_90

Re:Spojový seznam v C - zápis do souboru
« Odpověď #24 kdy: 03. 12. 2016, 20:24:09 »
Všem díky,
momentálně se točim kolem mazání souborů v C.
Našel jsem, že existuje fce remove() z stdio.h. jenže mne to nefunguje :(

tady je můj kód:

Kód: [Vybrat]
#define CESTA "/home/teodor/Plocha/parametry_exoplanet.csv"
#include <stdio.h>

Kód: [Vybrat]
if( remove(CESTA) != 0) {
       
        perror("Soubor se nesmazal!\n");
    } else {
       
        printf("Soubor byl uspesne smazan!\n");
       
    }

Mažu spojový seznam a gcc mi ještě vypíše tuhle hlášku: free(): invalid pointer: 0x00007fff7ca16930

Chci jen nějak nakopnout, nechci aby to tu někdo řešil za mne.

robotron

Re:Spojový seznam v C - zápis do souboru
« Odpověď #25 kdy: 03. 12. 2016, 20:48:26 »
OT: osobne bych parametry_exoplanet.csv zpracovaval radsi v Julii nez v C.

Teo_90

Re:Spojový seznam v C - zápis do souboru
« Odpověď #26 kdy: 03. 12. 2016, 21:32:05 »
OT: osobne bych parametry_exoplanet.csv zpracovaval radsi v Julii nez v C.


No já bych to raději dělal pomocí objektů v C++, ale tohle je semestrálka na VŠ a mí striktní zadání :(

Teo_90

Re:Spojový seznam v C - zápis do souboru
« Odpověď #27 kdy: 03. 12. 2016, 21:52:09 »
Mazání seznamu je opraveno, byla to moje zbrklost.
Chyba v pointeru.

ByCzech

  • *****
  • 1 806
    • Zobrazit profil
    • E-mail
Re:Spojový seznam v C - zápis do souboru
« Odpověď #28 kdy: 03. 12. 2016, 23:03:27 »
Všem díky,
momentálně se točim kolem mazání souborů v C.
Našel jsem, že existuje fce remove() z stdio.h. jenže mne to nefunguje :(

Já teda na mazání souborů raději používám unlink a ne "alias", který volá unlink nebo rmdir podle toho, jestli cesta je soubor nebo adresář... Něco se vymastí a smaže to adresář i když předpokládám soubor. To já nerad :D

Teo_90

Re:Spojový seznam v C - zápis do souboru
« Odpověď #29 kdy: 04. 12. 2016, 11:56:51 »
Všem díky,
momentálně se točim kolem mazání souborů v C.
Našel jsem, že existuje fce remove() z stdio.h. jenže mne to nefunguje :(

Já teda na mazání souborů raději používám unlink a ne "alias", který volá unlink nebo rmdir podle toho, jestli cesta je soubor nebo adresář... Něco se vymastí a smaže to adresář i když předpokládám soubor. To já nerad :D

No, docela na to čumim jako puk, protože soubor si vytvořim a pak ho chci smazat, tak použiji fci remove() z stdio.h a ona prostě nejde :D Soubor zůstane tam kde je :D Byl bych rád, kdyby mne tu někdo nakopl, jak to udělat i s tou cestou co mam výše.
Já rád používám statická makra kvůli přehlednosti kódu.