Panel użytkownika
Nazwa użytkownika:
Hasło:
Nie masz jeszcze konta?

Wczytywanie stringa z pliku

Ostatnio zmodyfikowano 2014-02-14 10:57
Autor Wiadomość
Atexor
Temat założony przez niniejszego użytkownika
Wczytywanie stringa z pliku
» 2014-02-13 18:31:37
Witam,
mam problem z następującym programem. Otóż muszę napisać program (w języku C), który zliczy wystąpienia poszczególnych znaków pojawiających się w pliku tekstowym (np. dane.txt), a następnie je posortuje i zapisze do innego pliku tekstowego. Program napisałem dla wczytywania string'ów z klawiatury, jednak mam problem z przerobieniem go na wczytywanie tekstu z pliku. Konkretnie chodzi mi o przerobienie gets na fgets. Szukałem w dokumentacji jak to zrobić, ale nie znalazłem nic konkretnego. Jako komentarz umieściłem swoje ostatnie "twory", które się kompilują, ale crash'ują program. Jak to zrobić?
Kod:
C/C++
#include <stdio.h>
#include <string.h>

int ilosc[ 256 ];

int main()
{
    int n, i, j;
    char napis[ 256 ];
    FILE * odczyt;
    FILE * zapis;
    odczyt = fopen( "dane.txt", "rt" );
    zapis = fopen( "zapis.txt", "wt" );
    //fgets(napis, 256,odczyt);
    //if(fgets(napis, 256,odczyt)!= NULL)
    gets( napis );
    n = strlen( napis );
    printf( "Dlugosc stringa n=%d\n", n );
   
    for( i = 0; i < n; i++ )
    {
        ilosc[( int ) napis[ i ] ] ++;
    }
   
    for( i = 0; i < 256; i++ )
    {
        if( ilosc[ i ] > 0 )
        {
            printf( "%c: %d\n",( char ) i, ilosc[ i ] );
        }
    }
   
    printf( "Posortowane znaki wedlug ASCII:\n" );
    for( i = 0; i < 256; i++ )
    {
        for( j = 0; j < ilosc[ i ]; j++ )
        {
            printf( "%c", i );
            fprintf( zapis, "%c", i );
        }
    }
    printf( "\n" );
    fclose( odczyt );
    fclose( zapis );
   
    system( "PAUSE" );
    return( 0 );
}

Z góry dziękuję.
P-104607
alixir
» 2014-02-13 20:29:24
Jeśli chodzi o odczyt z pliku to możesz to zrobić np. tak:

C/C++
i = 0;
while(( napis[ i++ ] = fgetc( odczyt ) ) != EOF )
    ;

napis[ i ] = '\0';
P-104614
Atexor
Temat założony przez niniejszego użytkownika
» 2014-02-13 22:56:27
Umieściłem Twój kod (bez tego średnika, bo to chyba literówka), jdnak od razu po uruchomieniu programu crash'uje się.

Poszukałem w dokumentacji informacji o fgetc, którego użyłeś i na jej podstawie zrobiłem takie coś:
C/C++
while( fgetc( odczyt ) != EOF )
{
    napis[ i ] = fgetc( odczyt );
    i++;
}
Jednakże z powodu inkrementacji i program także nie działa (bez niej się nie crash'uje, ale oczywiście nie wylicza). Przy deklaracji "i" mam i=0.
P-104616
alixir
» 2014-02-14 07:47:25
Ten średnik właśnie ma kluczowe zadanie :) (to blok pustej instrukcji)
Kod jest poprawny. Skopiuj go tak jak jest wklejony, a na pewno zadziała.
Wklej go dokładnie w miejsce tych linijek (resztę zostaw bez zmian)

C/C++
//fgets(napis, 256,odczyt);
//if(fgets(napis, 256,odczyt)!= NULL)
gets( napis );
P-104623
Atexor
Temat założony przez niniejszego użytkownika
» 2014-02-14 10:04:23
O, teraz działa. Dziękuję bardzo.
A mógłbyś wyjaśnić na jakiej zasadzie to działa, ponieważ nie chcę tego "kuć jak jest"?
Chodzi mi głównie o inkrementację, a także  napis[ i ] = '\0';

Średnik służy zastąpieniu nawiasów pętli {} jak mniemam. Zastąpiłem go {}, zaś napis[ i ] = '\0'; zarówno wewnątrz pętli jak i poza nią.

Inkrementacja służy zapewne czytaniu kolejnych wyrazów, zaś '\0' przerywa wczytywanie jak natrafi na koniec pliku. Tylko konstrukcja jest jakaś taka...niezrozumiała dla mnie.

A mógłbyś mi powiedzieć jeszcze co jest nie tak w tym moim kodzie? Zastąpiłem te linijki które powiedziałeś moim kodem
 i działa, ale nie wczytuje wszystkich znaków.
Dziękuję.
P-104626
alixir
» 2014-02-14 10:57:51
Mój kod używa funkcji fgetc, która pobiera znak po znaku, a nie cały string.
Warunek pętli jest tutaj zarówno blokiem wykonawczym.
( napis[ i++ ] = fgetc( odczyt ) ) != EOF

Na początku i ma wartość 0, więc funkcja wczytuje z pliku pierwszy znak i zapisuje go do tablicy znakowej na pozycję 0 (czyli pierwszy element tablicy). Od razu następuje sprawdzenie, czy pobrany znak nie jest czasami końcem pliku (jeśli tak, pętla kończy działanie).
Postrinkrementacja wewnątrz warunku zapewnia, że po wykonaniu działania wartość zmiennej „i” zostaje zwiększona, aby w przyszłym obiegu wskazywała na kolejny element tablicy.
Pętla się powtarza, dla każdego wczytanego znaku, aż do wczytania EOF.
W obecnym stanie tablica przechowuje znaki, ale nie jest to łańcuch znakowy, gdyż takowy musi kończyć się znakiem \0
Dlatego na końcu wykonuje się instrukcja dopisująca w aktualną pozycję znak końca łańcucha. I tu w sumie mój błąd, bo powinna to być pozycja i-1 aby znak EOF nie należał do łańcucha znaków.
Natomiast co do średnika to oczywiście może on zostać zastąpiony klamrami
{ }
 lub słowem
continue;
Ważne jest aby widoczne było gdzie kończy się pętla.

Aha odnośnie twojego kodu:

C/C++
while( fgetc( odczyt ) != EOF )
{
    napis[ i ] = fgetc( odczyt );
    i++;
}

Masz dwukrotne wywołanie funkcji fgetc. W warunku funkcja wczytuje jeden znak, sprawdza czy nie jest to znak końca, porzuca go (bo nie ma przypisania), wchodzi do środka pętli, pobiera kolejny znak i przypisuje do zmiennej .... itd. Czyli innymi słowy mówiąc, program pobiera tylko co drugi znak!
P-104628
« 1 »
  Strona 1 z 1