Pozycje liter w pliku tekstowym.

0

Witam. Napisałem program, który pobiera od użytkownika nazwę pliku do otwarcia oraz umożliwia podanie podanie położenia w pliku i wyświetla część pliku od tego miejsca do najbliższego znaku nowej linii. Oto kod:

#include <stdio.h>
#include <string.h>
#include <ctype.h>
#include <stdlib.h>
#define MAX 41

int main(void)
{
	FILE *wsk;
	int i;
	long int a;
	char nazwa[MAX];
	char ch;

	puts("Podaj nazwe pliku");
	puts("Wprowadz pusta linie aby zakonczyc");
	if (gets(nazwa) != NULL && nazwa[0] != '\0')
	{
		if ((wsk = fopen(nazwa, "r")) == NULL)
		{
			puts("Blad otwarcia pliku");
			exit(1);
		}
		puts("Podaj polozenie w pliku");
		while (scanf("%ld", &a) == 1)
		{
			if (fseek(wsk, a, SEEK_SET) != 0)
			{
				puts("Wyszedles poza granice pliku!");
				puts("Podaj polozenie pliku");
				puts("Wprowadz dana nienumeryczna aby zakonczyc");
				continue;
			}
			else
			{
				while((ch = getc(wsk)) != EOF && ch != '\n')
				{
					putc(ch, stdout);
				}
			}
			puts("\nPodaj polozenie pliku");
			puts("Wprowadz dana nienumeryczna aby zakonczyc");
		}
	}
	getchar();
	getchar();
	return 0;
}

I tutaj mam parę pytań. Przykładowo w pliku zapisano coś takiego:

ABCDEFGHIJKLMNO
PRSTUWXYZ

Podając np. 0 wszystko jest ok i program wyświetla ABCDEFGHIJKLMNO, jednak kiedy np podam 15 lub 16 to wyświetla mi się coś takiego:

Podaj polozenie w pliku
15

Podaj polozenie pliku.
Wprowadz dana nienumeryczna aby zakonczyc

I chodzi mi tutaj dokładnie o ten Enter po 15. Program tak jakby odczytywał przy 15 i 16 znak nowej linii czy coś takiego. Po wprowadzeniu 15 w miejsce w pliku zostaje ustawione po literze O. Czyli w miejscu gdzie nic dalej nie ma. Moim zdaniem wpisanie 15 powinno spowodować przejście przed P i odczytanie PRSTUWXYZ. Jednak linijkę PRSTUWXYZ otrzymuję dopiero po wpisaniu 17. Czemu się tak dzieje?

Kolejne moje pytanie dotyczy wartości zwracanej przez fseek. Przeczytałem, że gdy wszystko jest ok fseek zwraca 0 a gdy coś poszło nie tak, np. próba przekroczenia granicy pliku zwraca -1. Więc napisałem coś takiego:

if (fseek(wsk, a, SEEK_SET) != 0)
{
	puts("Wyszedles poza granice pliku!");
	puts("Podaj polozenie pliku");
	puts("Wprowadz dana nienumeryczna aby zakonczyc");
	continue;
}

Wydawało by mi się, że w przypadku gdy wpiszę np. 50 (co jest poza granicą wcześniej podanego pliku tekstowego) program powinien wyświetlić to co jest w pętli i przejść od nowa do pobrania miejsca w pliku. Jednak tak się nie dzieje i po wpisaniu 50 otrzymuję coś takiego jak powyżej:

Podaj polozenie w pliku
50

Podaj polozenie pliku.
Wprowadz dana nienumeryczna aby zakonczyc

I ostatnie moje pytanie dotyczy funkcji scanf. W pętli while (scanf("%ld", &a) == 1) odczytywane są wartości do momentu wprowadzenia np. jakiejś litery. Jednak np. gdy wprowadzę zamiast jednej wartości, trzy: 2 4 6 to wyświetla mi się wszystko potem trzy razy. Te wartości 4 i 6 widać pozostają w buforze i potem są odczytywane, jednak wolałbym żeby scanf odczytywał tylko jedną wartość a resztę np. porzucał. Czy istniej taka możliwość?

1
olek1 napisał(a)

I chodzi mi tutaj dokładnie o ten Enter po 15. Program tak jakby odczytywał przy 15 i 16 znak nowej linii czy coś takiego.

No, ale sam mu każesz wstawiać znak nowej linii:

puts("\nPodaj polozenie pliku");

Normalnie, jak wypisujesz znaki, znak nowej linii z puts() jest umieszczany za nimi, dzięki czemu napis "Podaj..." pojawia się niżej. Kiedy z pliku odczytasz '\n', nie wypisujesz go na wyjściu, ale tak czy siak ląduje tam znak z puts().

olek1 napisał(a)

Po wprowadzeniu 15 w miejsce w pliku zostaje ustawione po literze O. Czyli w miejscu gdzie nic dalej nie ma.

Zawsze dalej coś jest, o ile nie jest to koniec pliku.

olek1 napisał(a)

Moim zdaniem wpisanie 15 powinno spowodować przejście przed P i odczytanie PRSTUWXYZ.

Nie. Pod offsetem 15 znajduje się znak nowej linii, a w warunku Twojej pętli stoi, że ma ona się zakończyć w takim przypadku.

olek1 napisał(a)

Jednak linijkę PRSTUWXYZ otrzymuję dopiero po wpisaniu 17. Czemu się tak dzieje?

Otwórz sobie ten plik w edytorze heksadecymalnym. Zobaczysz wtedy jak on wygląda... "fizycznie".

olek1 napisał(a)

Kolejne moje pytanie dotyczy wartości zwracanej przez fseek. Przeczytałem, że gdy wszystko jest ok fseek zwraca 0 a gdy coś poszło nie tak, np. próba przekroczenia granicy pliku zwraca -1.

Nie wiem gdzie to wyczytałeś, ale zmień źródło informacji. fseek() poza granicę pliku zwraca 0, nie -1.

#include <stdio.h>

int  main(void) {
    FILE        *in;
    int         retval;

    in = fopen("test.dat", "r");
    retval = fseek(in, 50, SEEK_SET);
    fclose(in);

    printf("%d\n", retval);
    return 0;
}
beorn@reki ~/tmp $ stat test.dat | grep Size
  Size: 26              Blocks: 8          IO Block: 4096   regular file
beorn@reki ~/tmp $ ./test
0
olek1 napisał(a)

I ostatnie moje pytanie dotyczy funkcji scanf. W pętli while (scanf("%ld", &a) == 1) odczytywane są wartości do momentu wprowadzenia np. jakiejś litery. Jednak np. gdy wprowadzę zamiast jednej wartości, trzy: 2 4 6 to wyświetla mi się wszystko potem trzy razy. Te wartości 4 i 6 widać pozostają w buforze i potem są odczytywane, jednak wolałbym żeby scanf odczytywał tylko jedną wartość a resztę np. porzucał. Czy istniej taka możliwość?

Tak, istnieje.

0
Kumashiro napisał(a)

Nie wiem gdzie to wyczytałeś, ale zmień źródło informacji. fseek() poza granicę pliku zwraca 0, nie -1.

Wszędzie tak pisze. W książce Stephena Praty, na wikibooks: http://pl.wikibooks.org/wiki/C/fseek I chyba jest to prawda, że jak wszystko jest ok to zwraca 0 bo jak zmienię warunek w programie na

if (fseek(wsk, a, SEEK_SET) == 0)

To mi zawsze wypisuje, że wyszedłem poza plik.
Zauważyłem też, że jak wpiszę liczbę ujemną np. -15 to wtedy wywala mi błąd pliku, a jak np. 80 to nie wypisuje nic. Czyli koniec pliku nie jest po wpisanych danych? Tak jak u mnie np. po Z?

ABCDEFGHIJKLMNO
PRSTUWXYZ

Kumashiro napisał(a)

Tak, istnieje.

Znalazłem coś na ten temat, że "%*d" porzuca dane wczytane przez scanf. Jednak jak dodam na koniec

                        {
                                while((ch = getc(wsk)) != EOF && ch != '\n')
                                {
                                        putc(ch, stdout);
                                }
                        }
                        puts("\nPodaj polozenie pliku");
                        puts("Wprowadz dana nienumeryczna aby zakonczyc");
                        scanf("%*d");

To porzuca tylko jedną wartość, czyli jak wpiszę np. trzy to wyświetli 2.

1
olek1 napisał(a)
Kumashiro napisał(a)

Nie wiem gdzie to wyczytałeś, ale zmień źródło informacji. fseek() poza granicę pliku zwraca 0, nie -1.

Wszędzie tak pisze. W książce Stephena Praty, na wikibooks: http://pl.wikibooks.org/wiki/C/fseek

Praty nie posiadam, ale pod linkiem na Wikibooks nie ma słowa o tym, że wyjechanie poza rozmiar pliku jest błędem. Ten opis nawet nie używa stałych SEEK_*...

olek1 napisał(a)

I chyba jest to prawda, że jak wszystko jest ok to zwraca 0 bo jak zmienię warunek w programie na

if (fseek(wsk, a, SEEK_SET) == 0)

To mi zawsze wypisuje, że wyszedłem poza plik.

Udowodniłem Ci, że jednak nie jest to prawdą, kod i listing wyjścia masz wyżej :)
Twoja zmiana w warunku także jest dowodem na to, że fseek() poza koniec pliku zwraca 0, nie -1.

olek1 napisał(a)

Zauważyłem też, że jak wpiszę liczbę ujemną np. -15 to wtedy wywala mi błąd pliku, a jak np. 80 to nie wypisuje nic. Czyli koniec pliku nie jest po wpisanych danych? Tak jak u mnie np. po Z?

Jest, ale próba wyjechania poza koniec pliku nie jest błędem, w przeciwieństwie do wyjechania przed początek.

olek1 napisał(a)

Znalazłem coś na ten temat, że "%*d" porzuca dane wczytane przez scanf. Jednak jak dodam na koniec

[...]

To porzuca tylko jedną wartość, czyli jak wpiszę np. trzy to wyświetli 2.

Ja mam inną propozycję. Najpierw wczytaj input od usera (jako tablicę znaków; '\n' kończy wejście), a potem skonwertuj to na wartość liczbową. Wtedy "1 2 3" będzie nieprawidłowe.

0
Kumashiro napisał(a)

Praty nie posiadam, ale pod linkiem na Wikibooks nie ma słowa o tym, że wyjechanie poza rozmiar pliku jest błędem. Ten opis nawet nie używa stałych SEEK_*...

W Pracie pisze dokładnie tak: "Wartościami zwracanymi przez fseek() są: 0, gdy wszystko jest w porządku, i -1, gdy wystąpił błąd, taki jak próba przekroczenia granic pliku.
Jednak już chyba już wiem o co chodzi, próba wyjechania poza koniec pliku a poza jego granice to dwie różne rzeczy.

Kumashiro napisał(a)

Twoja zmiana w warunku także jest dowodem na to, że fseek() poza koniec pliku zwraca 0, nie -1.

Nie napisałem wcześniej, że gdy ustawię warunek na:

if (fseek(wsk, a, SEEK_SET) == 0)

i wpiszę np. 4 to otrzymuję "Wyszedles poza granice pliku!".

A to:

Zauważyłem też, że jak wpiszę liczbę ujemną np. -15 to wtedy wywala mi błąd pliku, a jak np. 80 to nie wypisuje nic. Czyli koniec pliku nie jest po wpisanych danych? Tak jak u mnie np. po Z?

dotyczyło warunku

if (fseek(wsk, a, SEEK_SET) != 0)
Kumashiro napisał(a)

Jest, ale próba wyjechania poza koniec pliku nie jest błędem, w przeciwieństwie do wyjechania przed początek.

Chyba już wiem o co chodzi. Dzięki za odpowiedź

Co do tej konwersji napisałem coś takiego:

                puts("Podaj polozenie w pliku");
		while (scanf("%s", a) != EOF)
                {
					liczba = strtol(a, &koniec, 10);
                        if (fseek(wsk, liczba, SEEK_SET) != 0)
                        {
                                puts("Wyszedles poza granice pliku!");
                                puts("Podaj polozenie pliku");
                                puts("CTRL + Z - wyjscie");
                                continue;
                        }
                        else
                        {
                                while((ch = getc(wsk)) != EOF && ch != '\n')
                                {
                                        putc(ch, stdout);
                                }
                        }
                        puts("\nPodaj polozenie pliku");
                        puts("CTRL + Z - wyjscie");
                }

Ale w a pozostaje i tak ten cały ciąg i jest wykonywany tyle razy ile cyfr wprowadzę.

1
olek1 napisał(a)

Co do tej konwersji napisałem coś takiego:

[...]

Ale w a pozostaje i tak ten cały ciąg i jest wykonywany tyle razy ile cyfr wprowadzę.

Nie używaj scanf() :)

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


int main(void) {
    long    inval;
    char    input[100], *dend;


    input[99] = 0;
    while ( 1 ) {
        if ( fgets(input, sizeof(input) - 1, stdin) == NULL )
            break;

        errno = 0;
        inval = strtol(input, &dend, 10);
        if ( errno || (*dend != '\n') ) {
            fprintf(stderr, "Błędna wartość: %s", input);
            continue;
        };

        if ( dend == input ) {
            fprintf(stderr, "Nie podano wartości\n")
            continue;
        };

        if ( inval == 0 )   /* Wpisanie `0' kończy zabawę */
            break;

        printf("> %li\n", inval);
    };

    return 0;
}

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