• Najnowsze pytania
  • Bez odpowiedzi
  • Zadaj pytanie
  • Kategorie
  • Tagi
  • Zdobyte punkty
  • Ekipa ninja
  • IRC
  • FAQ
  • Regulamin
  • Książki warte uwagi

[C] fgets vs scanf

0 głosów
1,070 wizyt
pytanie zadane 28 marca 2017 w C i C++ przez Kamil Duljas Użytkownik (990 p.)

Witam. Zastanawiam sie nad działaniem funkcji fgets. Otóż powinna działać podobnie do funkcji scanf, a przynajmniej wczytywać poprawnie znaki. Mam tutaj prostą funkcję zaczerpniętą z książki "C. Rusz głową". Autor podaje zastosowanie funkcji fgets w programie, który ma na celu znalezienie fragmentu łańcucha i zwrócenie odpowiedniego wiersza tablicy. Problemem jest to, że użycie fgets nie działa poprawnie, a zastosowanie funkcji scanf daje poprawny wynik. Co więcej, próba wywołania dwa razy funkcji, pierw dla scanf, potem dla fgets, kończy się jej pominięciem:

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

char tracks[][80] =
{"raz",
"dwa",
"trzy",
"cztery"
};

void find(char search[])
{
int i;
for(i=0; i<5; i++){
    if(strstr(tracks[i],search))
    fprintf(stdout,"Utwor %i: '%s'\n", i, tracks[i]);
}}

int main()
{   char lista[80];
    printf("Wpisz utwor (scanf):\n");
    scanf("%79s",lista);
    find(lista);
    printf("Wpisz utwor (fgets):\n");
    fgets(lista,79,stdin);
    find(lista);
printf("koniec");
    return 0;
}

Co może być przyczyną tego, że funkcja fgets nie przekazuje łańcucha do funkcji find?

 

1 odpowiedź

+1 głos
odpowiedź 28 marca 2017 przez Evelek Nałogowiec (29,060 p.)
edycja 30 marca 2017 przez Evelek
 
Najlepsza

Powodem takiego zachowania jest to, że scanf() wczytuje wszystkie znaki aż do napotkania znaku nowej linii '\n' lub wczytaniu 79 znaków które podałeś w tej funkcji: %79s. Gdy napotka znak '\n', zostawia go w buforze. Następnie gdy chcesz wczytać kolejny łańcuch znaków przez funkcję fgets() nie możesz tego zrobić, bo w buforze nadal siedzi znak nowej linii '\n'. Zostaje on wczytany przez funkcję fgets(), która również wczytuje znaki aż do napotkania znaku nowel linii '\n' lub przepełnienia bufora w tym przypadku podanego przez ciebie 79.

Rozwiązaniem tego problemu jest po każdym wczytaniu do bufora ciągu znaków jego "czyszczenie". Można to zrobić np. w ten sposób:

scanf("%79s", lista);
	while (getchar() != '\n')
		continue;

 

komentarz 29 marca 2017 przez Kamil Duljas Użytkownik (990 p.)
Okej, drugim problemem jest to, że funkcja "find" nie działa przy użyciu fgets. Czyli wyrzucamy scanf i próbujemy wczytać argumenty za pomocą fgets.
komentarz 29 marca 2017 przez Evelek Nałogowiec (29,060 p.)
Jak to nie działa?
komentarz 29 marca 2017 przez Kamil Duljas Użytkownik (990 p.)

Przepraszam za tą niejasność. Program nie wykonuje funkcji po wczytaniu poprzez fgets, bynajmniej nie wyświetla nic na standardowe wyjście.

 

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

char tracks[][80] =
{"raz",
"dwa",
"trzy",
"cztery"
};

void find(char search[])
{
int i;
for(i=0; i<5; i++){
    if(strstr(tracks[i],search))
    fprintf(stdout,"Utwor %i: '%s'\n", i, tracks[i]);
}}

int main()
{   char lista[80];
    printf("Wpisz utwor (fgets):");
    fgets(lista,79,stdin);
    find(lista);
printf("koniec");
    return 0;
}

 

komentarz 29 marca 2017 przez Evelek Nałogowiec (29,060 p.)
Z pamięci piszę bo nie mam kompilatora pod ręką, ale czy nie powinno być: char tracks[4][80] ? Poza tym lepsze jest: const char *tracks[4] = { ... }
komentarz 29 marca 2017 przez Evelek Nałogowiec (29,060 p.)
A jako argument funkcji find zapisalbym char *search zamiast char search[].
komentarz 29 marca 2017 przez Kamil Duljas Użytkownik (990 p.)
Sprawdzałem i skutek ten sam. Chodzi o to, że ta funkcja działa bez zarzutu gdy wczytuje argument za pomocą scanf, a gdy próbuje to zrobić za pomocą fgets to program się wykona, ale na ekranie jest puste pole.
komentarz 29 marca 2017 przez Evelek Nałogowiec (29,060 p.)

Bo fgets() zostawia w łańcuchu znak nowej linii. Porównywane jest np. raz\n == raz i nigdy nie jest to spełnione. Trzeba samemu zamieniać '\n' na '\0', więc np.

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

const char *tracks[80] = { "raz", "dwa", "trzy", "cztery" };

void find(char search[])
{
	int i;
	for (i = 0; i<4; i++) {
		if (strstr(tracks[i], search))
			fprintf(stdout, "Utwor %i: '%s'\n", i, tracks[i]);
	}
}

char *wczytaj(char *z, int ile)
{
	char *wynik;
	int i = 0;

	wynik = fgets(z, ile, stdin);
	if (wynik) {
		while (z[i] != '\n' && z[i] != '\0')
			i++;
		if (z[i] == '\n')
			z[i] = '\0';
		else
			while (getchar() != '\n')
				continue;
	}
	return wynik;
}

int main()
{
	char lista[80];
	printf("Wpisz utwor (fgets):");
	wczytaj(lista, 80);
	find(lista);
	printf("koniec");

	getchar();
	return 0;
}

 

komentarz 30 marca 2017 przez Kamil Duljas Użytkownik (990 p.)
Dziękuję :) Z równoległego źródła też znalazłem informację o tym, że fgets wstawia \n. Mam pytanie jeszcze o komendę "continue". Do której linii kodu będzie przekierowywać? Jest póki co mało intuicyjna dla mnie.
komentarz 30 marca 2017 przez Evelek Nałogowiec (29,060 p.)
continue powoduje, że dana pętla jest wykonywana raz jeszcze. Czyli tak jak w przykładzie: while(getchar() != '\n') continue; Czyli funkcja getchar() pobiera pojedynczy znak z bufora. Jeśli jest nim coś innego niż '\n' to pętla sie wykonuje ponownie (continue). Kiedy pobierze znak '\n' pętla while zostaje przerwana. W przypadku pętli masz dwa takie słowa kluczowe: continue, które powoduje, że pętla jest wykonywana raz jeszcze, oraz break które powoduje, że pętla jest przerywana. W przypadku dłuższych pętli gdzie jest więcej instrukcji wszystkie te które sie znajdują po słowie continue/break nie zostają wykonane czyli np. int i = 0; while () { i++; if (i == 5) break; puts("Hello"); }. Sam sprawdź ile razy wypisze sie słowo Hello. ;)

Podobne pytania

0 głosów
2 odpowiedzi 379 wizyt
pytanie zadane 29 listopada 2017 w C i C++ przez Krystek102 Bywalec (2,430 p.)
0 głosów
4 odpowiedzi 1,061 wizyt
pytanie zadane 26 maja 2017 w C i C++ przez Kuba2263 Nowicjusz (200 p.)
0 głosów
2 odpowiedzi 263 wizyt
pytanie zadane 10 grudnia 2016 w C i C++ przez Jan Dobrakowski Użytkownik (580 p.)
Porady nie od parady
Pytania na temat serwisu SPOJ należy zadawać z odpowiednią kategorią dotyczącą tej strony.SPOJ

84,132 zapytań

132,903 odpowiedzi

293,920 komentarzy

55,556 pasjonatów

Motyw:

Akcja Pajacyk

Pajacyk od wielu lat dożywia dzieci. Pomóż klikając w zielony brzuszek na stronie. Dziękujemy! ♡

Oto dwie polecane książki warte uwagi. Pełną listę znajdziesz tutaj.

...