getchar() + EOF. Nieskończona pętla.

0

Cześć wszystkim!

Dopiero zaczynam swoją przygodę z C. Aktualnie przerabiam książkę "Język C Szkoła programowania" S. Prata.
Wszystkie ćwiczenia, które wykonuję, staram się robić w miarę możliwości samodzielnie. Jeśli coś mi nie idzie, wałkuję jeden rozdział aż go dobrze zrozumiem. Mimo wszystko od pewnego czasu trafiłem na blokadę, której w żaden sposób nie mogę sforsować.

Zatrzymałem się na ćwiczeniu nr 1 z 8. rozdziału powyższej książki "Znakowe wejście-wyjście i przekierowywanie".
Treść ćwiczenia jest następująca: "Napisz program, który zlicza znaki (wpisywane lub pobierane z pliku) aż do wystąpienia końca pliku".

Nie chcąc iść na łatwiznę, próbuję napisać program, w którym to od użytkownika będzie zależało, czy będzie zliczał znaki z pliku, czy z klawiatury.
O ile zliczając znaki z pliku, wszystko jest tak jak być powinno, problem jest przy zliczaniu znaków z klawiatury.
Program liczy znaki wpisane przez użytkownika prawidłowo. Po wpisaniu swojego tekstu, na koniec w nowym wierszu wprowadzając EOF (ctrl+Z), następuje nieskończona pętla, tak jakby (jeśli dobrze zrozumiałem zagadnienie) EOF zostaje w buforze wejściowym, którego w żaden sposób nie potrafię opróżnić.

Oto mój kod:

// Ćwiczenie_1.c -- program zlicza znaki z pliku lub wpisane z klawiatury, zaleznie od wybranej przez uzytkownika opcji
#include <stdio.h>
#include <stdlib.h>
int zliczanie_program(void);		// funkcje zwroca wartosc typu int, i nie pobiora z funkcji main zadnego argumentu,
int zliczanie_plik(void);			// dane do wykonania tych funkcji beda rowniez w nich pobierane  
char zla_opcja(int wpisana_opcja);	// gdy uzytkownik wpisze inny znak, niz 'a', 'b' lub 'q'

int main(void)
{
	int opcja;

	printf("Program policzy ilosc znakow wpisanych przez uzytkownika\n");
	printf("do programu lub do pliku tekstowego, zaleznie od wybranej opcji.\n\n");
	printf("Wybierz opcje:\n");
	printf("a) zliczanie znakow wpisanych w programie,\n");
	printf("b) zliczanie znakow wpisanych do pliku txt,\n");
	printf("q) zakoncz program.\n");

	while((opcja = getchar()) != 'q')
	{

		switch(opcja)
		{
			case 'a':	zliczanie_program();
						break;
			case 'b':	zliczanie_plik();
						break;
			case EOF:	printf("Przechodzi przez switch w funkcji main\n");
						printf("opcja = %d\n\n", opcja);	// EOF powoduje nieskonczona petle
						//break;		
			default:	zla_opcja(opcja);
		}


		printf("Wybierz opcje:\n");
		printf("a) zliczanie znakow wpisanych w programie,\n");
		printf("b) zliczanie znakow wpisanych do pliku txt,\n");
		printf("q) zakoncz program.\n");

		
		fflush(stdin);			// czyści bufor wejściowy z EOF po wyjściu z pliku txt

	}

	printf("Koniec programu!\n\n");

	return 0;
}



int zliczanie_program(void)
{
	int znak;
	int ilosc_drukowanych = 0;
	int ilosc_niedrukowanych = 0;
	int ilosc_znakow = 0;

	printf("Wybrales opcje zliczania znakow wpisanych do programu.\n");
	printf("Wpisz ponizej dowolny tekst, a program policzy ilosc wpisanych znakow.\n");

	while((znak = getchar()) != '\n');	//jesli wczesniej zatwierdzono enterem opcje a, bez tego polecenia wypisze pierwszy case ze switcha

	while((znak = getchar()) != EOF)
	{

		if((znak == '\n') || (znak == '\t') || (znak == ' '))
		{
			switch(znak)
			{
				case '\n':	ilosc_niedrukowanych++;
							printf("wpisano znak: '\\n', ilosc niedrukowanych: %d\n", ilosc_niedrukowanych);	// -> // wyswietla jeden /
							break;
				case '\t':	ilosc_niedrukowanych++;
							printf("wpisano znak: tab, ilosc niedrukowanych: %d\n", ilosc_niedrukowanych);
							break;
				case ' ':	ilosc_niedrukowanych++;
							printf("wpisano znak: spacja, ilosc niedrukowanych: %d\n", ilosc_niedrukowanych);
							break;
			}
		}
		else
		{
			ilosc_drukowanych++;
			printf("wpisano znak: %c, ilosc drukowanych: %d\n", znak, ilosc_drukowanych);
		}
		

	}

	ilosc_znakow = ilosc_drukowanych + ilosc_niedrukowanych;

	printf("\n");
	printf("Wprowadzono %d znakow, z czego:\n", ilosc_znakow);
	printf("- %d znakow drukowanych,\n", ilosc_drukowanych);
	printf("- %d znakow niedrukowanych.\n\n", ilosc_niedrukowanych);

	return 0;
}



int zliczanie_plik(void)
{
	int ilosc_drukowanych = 0;
	int ilosc_niedrukowanych = 0;
	int ilosc_znakow = 0;

	int znak_plik;
	FILE * fp;
	
	fp = fopen("plik_do_odczytu.txt", "r");

	if(fp == NULL)
	{
		printf("Blad przy probie otwarcia pliku.\n");
		exit(1);
	}

	printf("Do pliku tekstowego wpisano tekst:\n");

	while((znak_plik = getc(fp)) != EOF)
	{
		putchar(znak_plik);

		if((znak_plik == '\n') || (znak_plik == '\t') || (znak_plik == ' '))
			ilosc_niedrukowanych++;
		else
			ilosc_drukowanych++;
	}

	ilosc_znakow = ilosc_drukowanych + ilosc_niedrukowanych;

	printf("\n\n");

	printf("W pliku tekstowym naliczono %d znakow, z czego:\n", ilosc_znakow);
	printf("- %d znakow drukowanych,\n", ilosc_drukowanych);
	printf("- %d znakow niedrukowanych.\n\n", ilosc_niedrukowanych);
	
	fclose(fp);

	return 0;
}



char zla_opcja(int wpisana_opcja)        
{
	printf("Wpisano %d.\n", wpisana_opcja);		// %d wypisze kod znaku z ASCII, %c wypisze znak
	printf("Nalezy wpisac a, b lub q.\n\n");

	return 0;
}

Próbowałem już wielu rzeczy, łącznie z funkcją 'fflush(stdin)', której teoretycznie nie chciałem używać w tym programie, ponieważ jeszcze nie doszedłem do niej w książce. Program starałem się napisać sam, tyle, że siedzę nad nim już od jakiegoś miesiąca i powoli ręce opadają.

Będę wdzięczny za wszelką pomoc.

Dzięki!
Olav93

0

Przecież pętlę kończysz gdy wczytasz q, a nie gdy nastąpi EOF.

swoją drogą, tu masz odwrócony warunek:

 while((znak = getchar()) != '\n');  //jesli wczesniej zatwierdzono enterem opcje a, bez tego polecenia wypisze pierwszy case ze switcha
0

Przecież pętlę kończysz gdy wczytasz q, a nie gdy nastąpi EOF.

Chodzi o pętlę w funkcji zliczanie_program()

swoją drogą, tu masz odwrócony warunek: (...)

Ten while jest po to, żeby kolejny getchar() nie wczytał entera, którym zatwierdziłem opcję z funkcji main.

0
Olav93 napisał(a):

Chodzi o pętlę w funkcji zliczanie_program()

Program opuszcza tę pętlę i wpada w nieskończoną pętlę wyboru opcji w main. Dzieje się to zarówno wtedy, jeśli wyjdzie się z opcji a i b, tylko z opcji a wpada od razu w tę nieskończoną pętlę, a przy opcji b trzeba wpisać EOF, już będąc w tej pętli.

0

Przy opcji b mam plik tekstowy w folderze z programem. Przy zliczaniu znaków z pliku tekstowego, program sam odczytuje koniec pliku.
Właściwie, przy opcji b program nie wpada w nieskończoną pętlę.

1

A przy opcji a program opuszcza tę pętlę po wpisaniu EOF i wraca do main, gdzie ponownie czyta getchar EOF, ale nie może się wydostać, bo tam jest warunek, że ma wykonywać pętlę nie do EOF, ale do 'q'.
Wraca do pętli main, ponieważ wypisuje w kółko tekst:
Wybierz opcje:
itd.

2

Debuger, dobra okazja, żeby się go nauczyć. Nikt Cię w tym nie zastąpi.

W jakim środowisku to piszesz?

0
overcq napisał(a):

A przy opcji a program opuszcza tę pętlę po wpisaniu EOF i wraca do main, gdzie ponownie czyta getchar EOF, ale nie może się wydostać, bo tam jest warunek, że ma wykonywać pętlę nie do EOF, ale do 'q'.

Wraca do pętli main, ponieważ wypisuje w kółko tekst:
Wybierz opcje:
itd.

Dokładnie tak.
Cała sprawa rozchodzi wokół tego, dlaczego przy opcji a, po wprowadzeniu EOF, jest on cięgle pobierany przez getchar?

AnyKtokolwiek napisał(a):

Debuger, dobra okazja, żeby się go nauczyć. Nikt Cię w tym nie zastąpi.

W jakim środowisku to piszesz?

Właściwie, jeszcze do debugera nie doszedłem :(
Pelles C

2

Ponieważ EOF jest stanem strumienia, a nie znakiem, mimo że jest tutaj odczytywany w ten sam sposób.

1 użytkowników online, w tym zalogowanych: 0, gości: 1