Kirjautuminen

Haku

Tehtävät

Keskustelu: Ohjelmointikysymykset: C tiedoston luku rivi riviltä

88VK88 [27.10.2022 15:31:26]

#

Hei,

Innokas C opiskelija tarvitsisi apua. Miten C voi lukea tiettyjä rivejä tekstitiedostosta ja verrata niitä syötön muuttujiin poistoa varten? Pitäisi siis tekstitiedostosta, jossa on listassa sukunimi ja etunimi löytää oikea rivi, joka täsmää syötön etunimeen ja sukunimeen. Kyseessä on koulutehtävä, joten en toivo suoraa vastausta vaan jotain kaava palasia oikeaoppisen lauserakenteen löytämiseen. Minulla on siis lukihäiriö, joten hyödyn kaavojen näkemisestä eniten ymmärtääkseni koodin lyhyin selityksin. Olenkin hyötynyt paljon ohjelmointiputkan C -oppaasta, jossa oli paljon havainnollistavia ohjeita, mutta tähän en löytänyt ohjeistusta, enkä myöskään googlettamalla, joten olen täällä nyt seuraavaksi etsimässä apua.

Metabolix [27.10.2022 16:00:13]

#

Jos jokaisella rivillä on varmasti tasan kaksi sanaa, olisi helpointa lukea silmukassa aina kaksi sanaa (%s) fscanf:llä. Eli tarvittavia palasia ovat: fopen, while, fscanf, if, strcmp, fclose.

Jos rivillä voi olla enemmän sanoja (kuten hienompi nimi Matti von Möttönen), koko rivin voi lukea funktiolla fgets ja myös se syötetty nimi pitää yhdistää yhdeksi vaikka funktiolla snprintf, jotta sitä voi vertailla koko riviin.

Sitten jos kyseinen rivi pitää vielä poistaa, käytännössä joudut kirjoittamaan uudestaan koko tiedoston tai korvaamaan rivin esimerkiksi samalla määrällä välilyöntejä. Tästä on varmaan tarkempi ohje tehtävänannossa.

88VK88 [28.10.2022 10:58:27]

#

#include <stdio.h>
#include <string.h>
#include <stdbool.h>

#define TIEDOSTO_KOKO 1024
#define MAX_RIVI 2048

int main() {
    FILE *avaus, *temp;
    char tiedosto[TIEDOSTO_KOKO] = "luettelo.txt";
    char temp_tiedosto[TIEDOSTO_KOKO];

    char poista_rivi[MAX_RIVI];
    char buffer[MAX_RIVI];

    tiedosto[strlen(tiedosto) - 1] = '\0';

    strcpy(temp_tiedosto, "temp___");
    strcat(temp_tiedosto, tiedosto);

    printf("Poista rivi: ");
    fgets(poista_rivi, MAX_RIVI, stdin);

    avaus = fopen(tiedosto, "r");
    temp = fopen(temp_tiedosto, "w");

    if(tiedosto == NULL || temp == NULL) {
        printf("Virhe tiedostojen avauksessa.\n");
        return 1;
    }

    bool jatka_lukemista = true;

    do {
        fgets(buffer, MAX_RIVI, avaus);

        if(feof(avaus)) jatka_lukemista = false;
        else if(strcmp(buffer, poista_rivi) != 0)
            fputs(buffer, temp);
    } while(jatka_lukemista);

    fclose(avaus);
    fclose(temp);

    remove(tiedosto);
    rename(temp_tiedosto, tiedosto);

    return 0;
}

Hei,

löysin kyseisen koodin googlella. Yritin lähteä tätä muokkaamaan haluamaani suuntaan, mutta alkuperäisessä koodissa tiedoston nimi pitää syöttää näin:

fgets(filename, FILENAME_SIZE, stdin);
filename[strlen(filename) - 1] = '\0';

char tiedosto[TIEDOSTO_KOKO]; taas loppui tuohon. Eli miten saisin tuon toteutettua niin, että tiedosto, josta nimen haluan poistaa on jo tiedossa?

Koska olen vielä ihan C alkeissa niin olisin halunnut oppia lukemaan kansion tiedot alunperin tutulla for komennolla rivi riviltä ja valikoimaan halumani rivin, mutten löytänyt mitään havainnollistavia ohjeita oikeaoppiseen rakenteeseen. Osaan fseekillä lukea kaikki tiedoston rivit kerralla ja valita miltä riviltä luku alkaa, mutten valikoida tiettyjä rivejä vertailua varten muuttujan kanssa, joten en sitä komentoa osaa sen enempää hyödyntää tässä tehtävässä.

Metabolix [28.10.2022 11:11:12]

#

Ota koko tuo \0-rivi pois. Sen tarkoitus on ollut poistaa tiedostonimestä viimeinen merkki, koska fgets jättää tekstin loppuun rivinvaihdon (\n), jota ei tietenkään pidä olla tiedostonimessä. Tuossa siis poistatkin tiedostonimestä viimeisen t-kirjaimen.

Muutenhan tuo koodi näyttää hyvältä. Silmukassa et tarvitse muuttujaa jatka_lukemista, vaan voisit lopettaa silmukan break-sanalla. Vielä helpommalla pääset, kun hyödynnät fgets-funktion tulosta, joka on NULL virhetilanteessa (kuten siis tiedoston lopussa):

while (fgets(buffer, MAX_RIVI, avaus)) {
  //...
}

88VK88 [28.10.2022 11:34:46]

#

Hei,

Kiitoksia neuvoista. Tuli edistystä. Ongelma tosin jo tuossa alkuperäisessä koodissa oli, että tuo komento ei poista haluamaani nimeä halutulta riviltä vaan yhden alempaa. Kuinka voisin kohdistaa poiston oikealle riville?

Metabolix [28.10.2022 20:51:15]

#

Kyllähän tuo yllä oleva koodi poistaa ihan oikean rivin (edellä mainitulla korjauksella). Sellaisen asian kääntäjä vielä huomauttaa, että ylempänä if-lauseessa tiedosto ei ole koskaan NULL, koska sehän on tiedoston nimi. Vertailussa pitäisi olla avaus == NULL.

Vastaus

Aihe on jo aika vanha, joten et voi enää vastata siihen.

Tietoa sivustosta