Struktura przechowująca uchwyt do pliku

0

Posiadam strukturę, w której przechowywany jest uchwyt do pliku oraz jego rozmiar. Celem zadania jest zapisanie tych struktur do tablicy oraz znalezienie takiej, która zawiera uchwyt do pliku przechowującego najmniej znaków.

struct file_t
{
    FILE* f;
	int size;
};

W ten sposób deklaruję tablicę struktur:

struct file_t* files = (struct file_t*)malloc(10 * sizeof(struct file_t));

Do otwierania plików służy funkcja open_file:

int open_file(struct file_t* f, const char* filename) {
   if (f->f = fopen(filename, "a+")) {
	long curpos, length;
	curpos = ftell(f->f);
	fseek(f->f, 0L, SEEK_END);
	length = ftell(f->f);
	fseek(f->f, curpos, SEEK_SET);
	f->size = length;
   	return 1;
  }
  else
  return 2;
}

A przy jej pomocy zapisuję struktury w tablicy files.

if (open_file(&files[size], input) == 1) {
			size++;
  }

Pierwszy problem pojawia się, kiedy chcę odczytać z tablicy zawartość jakiejś struktury, bo program wczytuje same śmieci.

struct file_t* file;
file = &files[0];
char bufor[100];
fread(bufor, sizeof(char), file->size, file->f);
printf("tekst  - %s\n", bufor);

Proszę o pomoc lub o pytania jeśli coś jest niejsane.

0
if (open_file(&files[size], input) == 1) {
			size++;
  }

A ile wynosi size na koniec operacji? Jeśli wynosi 0 to możliwe jest, że nie udało się otworzyć żadnego pliku.

0
several napisał(a):

A ile wynosi size na koniec operacji? Jeśli wynosi 0 to możliwe jest, że nie udało się otworzyć żadnego pliku.

Size się zmienia. W strukturze też rozmiar pliku jest poprawnie przechowywany. Problem pojawia się kiedy chcę odczytać tekst.

2
SpaceInvader napisał(a):

W ten sposób deklaruję tablicę struktur:

struct file_t* files = (struct file_t*)malloc(10 * sizeof(struct file_t));
  1. To nie jest deklaracja, ale definicja wraz z instrukcją do wykonania.
  2. (struct file_t*) jest zbędne, bo w C void* jest domyślnie konwerotwane do dowolnego wskaźnika.
0

W strukturze też rozmiar pliku jest poprawnie przechowywany

A jesteś pewien, że tenże rozmiar jest mniejszy niż rozmiar Twojego bufora, czyli 100? A poza tym, dopiero zauważyłem, otwierasz swój plik w trybie append/update a nie do odczytu. Zamień fopen(filename, "a+") na fopen(filename, "r").

0

W trybie "a+" każdy zapis do pliku powoduje przesuniecie wskaźnika pliku na koniec, więc jak coś zapiszesz, to potem próba odczytu powoduje czytanie poza plikiem.
Dodaj obsługę błędów. Sprawdź co zwraca fread (liczbę odczytanych elementów) i co zwraca feof(f->f)

https://wandbox.org/permlink/lCYxgCQkdPIfDryf

0
SpaceInvader napisał(a):

Posiadam strukturę, w której przechowywany jest uchwyt do pliku oraz jego rozmiar. Celem zadania jest zapisanie tych struktur do tablicy oraz znalezienie takiej, która zawiera uchwyt do pliku zawierającego najmniej znaków.

struct file_t
{
    FILE* f;
	int size;
};

W ten sposób deklaruję tablicę struktur:

struct file_t* files = (struct file_t*)malloc(10 * sizeof(struct file_t));

Moim zdaniem lepiej użyć funkcji calloc zamiast malloc z mnożeniem.

W przypadku ogólnym mnożenie jest podatne na przepełnienie.

A jak chcesz mieć stałą liczbę elementów, to można użyć po prostu tablicy zamiast malloc.

long curpos, length;

IMO lepiej byłoby użyć off_t.

	curpos = ftell(f->f);

Zamiast funkcji ftell i fseek można użyć ftello i fseeko.

Zaletą funkcji *o jest korzystanie z typu off_t zamiast long - na systemach 32-bitowych long najczęściej też jest 32-bitowy, co powoduje ograniczenie offsetu do ok. 2 GiB, a pliki mogą być znacznie większe.

	length = ftell(f->f);

Ja bym to zrobił w taki sposób:

struct stat st;
fstat(fileno(f->f), &st);
length = st.st_size;

Takie rozwiązanie jest co prawda mniej przenośne, ale jeżeli zdecydujesz się na zmianę trybu otwaria pliku z "a+" np. na "r+", to nie będzie takiego niepotrzebnego skakania pozycją strumienia.

	fseek(f->f, curpos, SEEK_SET);

Niepotrzebnie używasz curpos, pozycja jest znana po otwarciu pliku (jest to początek lub koniec zależnie od trybu otwarcia).

	f->size = length;
   	return 1;
  }
  else
  return 2;
}

Jeśli chcesz używać 1 i 2, to dobrze by było gdzieś zdefiniować te wartości - #define lub enum.

A jeszcze lepiej byłoby trzymać się jakiejś konwencji, np. zwracać -1 w przypadku błędu, 0 gdy jest OK, albo 0 w przypadku błędu, 1 gdy jest OK.

Najlepiej wklej cały kod.

0
pms_enable_synaptics napisał(a):

Najlepiej wklej cały kod.

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


struct file_t
{
	FILE* f;
	int size;
};


int open_file(struct file_t* f, const char* filename);


struct file_t* find_min(const struct file_t* files, int size);


void close_file(struct file_t* f);


int open_file(struct file_t* f, const char* filename) {
	char bufor[50];
	if (filename[0] == '\n' || filename[0] == EOF)
		return 0;

	if (f->f = fopen(filename, "r")) {

		long curpos, length;
		curpos = ftell(f->f);
		fseek(f->f, 0L, SEEK_END);
		length = ftell(f->f);
		fseek(f->f, curpos, SEEK_SET);

		printf("%d\n", length);
		f->size = length;

		fread(bufor, sizeof(char), f->size, f->f);
		printf("%s\n", bufor);
		return 1;
	}

	else
		return 2;

}

struct file_t* find_min(const struct file_t* files, int size) {

	struct file_t min_file;
	min_file = files[size];

	
	while (size >= 0) {
		if (files[size].size < min_file.size)
		{
			min_file = files[size];
		}
		size--;
	}
	return &min_file;
}


int main() {

	int size = 0, i = 0;
	struct file_t* file;
	struct file_t* files = (struct file_t*)malloc(10 * sizeof(struct file_t));
	size_t length;
	char* t, * input;
	char c;

	printf("Enter files names: ");


	do {

		input = (char*)malloc(sizeof(char));
		length = 0;
		while ((c = getchar()) != '\n' && c != EOF) {
			t = (char*)realloc(input, length + 1);
			assert(t);
			input = t;
			input[length++] = c;
		}

		if (length >= 1) {
			t = (char*)realloc(input, length + 1);
			assert(t);
			input = t;
			input[length] = '\0';
		}
			

		printf("%s\n", input);

		if (open_file(&files[size], input) == 1) {
			size++;
			printf("udalo sie\n");
		}

		else
			printf("nie udalo sie\n");

	} while (length != 0);

	size_t x;
	file = &files[0];
	char bufor[100];
	x = fread(bufor, sizeof(char), file->size, file->f);
	printf("tekst[0] - %s\n", bufor);
	printf("size[0] - %d\n", files[0].size);
	printf("x[0] - %d\n", x);
	printf("wprowadz teksty: ");

 
	size -= 1;



	do {

		length = 0;
		input = (char*)malloc(sizeof(char));

		while ((c = getchar()) != '\n' && c != EOF) {
			t = (char*)realloc(input, length + 1);
			assert(t);
			input = t;
			input[length++] = c;
		}

		if (length >= 1) {
			t = (char*)realloc(input, length + 1);
			assert(t);
			input = t;
			input[length] = '\0';
		}
	

		printf("input - %s\n", input);
		printf("length - %d\n", length);
		file = find_min(files, size);
		fread(bufor, sizeof(char), file->size, file->f);
		printf("%s\n", bufor);
		fwrite(input, length, 1, file->f);
		printf("size before: %d\n", file->size);
		file->size += length;
		printf("size after: %d\n", file->size);

		free(input);


	} while (length != 0);


}

Ogólnie program wczytuje nazwy plików, aż do napotkania znaku nowej linii. Uchwyty tych plików są przechowywane w strukturach file_t razem z ich rozmiarami, a te struktury w tablicy files. Następnie wczytujemy tekst i dopisujemy go do pliku, który ma najmniejszy rozmiar. Powtarzamy to, dopóki nie wpiszemy znaku nowej linii.

Zdaję sobie sprawę, że jest więcej błedów, ale ten wspomniany na początku był pierwszym, z którym nie mogłem sobie poradzić. Z góry dziękuję za poświecony czas.

2

Proponuję taki kod (Linux, C99)

#include <stdio.h>
#include <unistd.h>
#include <sys/mman.h>
#include <sys/types.h>
#include <fcntl.h>
#include <sys/stat.h>
#include <stdbool.h>

typedef struct {
    int    fh;
    char   *text;
    size_t size;
} File;

#define NOFILE (File){.text = NULL}

File openFile(const char *name) {
    struct stat statbuf;
    int fh;
    if(stat(name, &statbuf) == 0 && (fh = open(name, O_RDWR)) > 0) {
        return (File) { .text = mmap(NULL, statbuf.st_size, PROT_READ|PROT_WRITE, MAP_PRIVATE, fh, 0),
                        .fh = fh,
                        .size = statbuf.st_size };
    }

    return NOFILE;
}

void closeFile(File f) {
    if(isok(f)) munmap(f.text, f.size);
    if(f.fh > 0) close(f.fh);
}

bool isok(File f) {
    return f.text != NULL && f.text != (void*)-1;
}

int main() {
    File f = openFile("foo.c");
    if(isok(f)) {
        printf("%.*s\n", f.size, f.text);
    } else {
        printf("can't open file\n");
        perror("foo");
    }
    closeFile(f);
    return 0;
}

Prosto, schludnie i na temat. Bez kombinowania.

Kilka uwag:

  1. Zamiast bawić się w standardowe fready, lepiej użyć mapowania pliku na pamięć. Tak jest znacznie wydajniej i chyba prościej. Jest to wprawdzie nieprzenośne, ale na dzień dzisiejszy raczej nie pisze się przenośnego kodu w C. Poza jakimiś systemami spadkowymi to tylko od poziomu OSa w dół, a nawet kernela, bo też narzędzia systemowe co raz częściej pisze się w innych językach. Z takim rozwiązaniem trzeba tylko pamiętać, że jak coś zapiszesz w tym kawałku pamięci to się od razu zapisze, możesz tego nie chcieć (no ale memcpy() nie boli)
  2. Zwracanie struktury przez wskaźnik w argumencie to anarchronizm. Jak kiedyś sprawdzałem, to GCC (z opcją -O2) nawet lepiej zoptymalizuje strukturę przekazywaną normalnie przez wartość. Sam byłem zdziwiony.
  3. Używaj C99/C11. Warto!
1
elwis napisał(a):

Zamiast bawić się w standardowe fready, lepiej użyć mapowania pliku na pamięć.

To zależy, jak dużą przestrzenią adresową dysponujemy i jaki jest rozmiar pliku - może okazać się, że całego pliku nie będziemy mogli zmapować i będziemy musieli mapować po kawałku.

Z tego co widzę, OP nie opanował jeszcze dobrze stdio, imho mmap to nieco wyżej postawiona poprzeczka.

Tak jest znacznie wydajniej i chyba prościej. Jest to wprawdzie nieprzenośne, ale na dzień dzisiejszy raczej nie pisze się przenośnego kodu w C. Poza jakimiś systemami spadkowymi to tylko od poziomu OSa w dół, a nawet kernela, bo też narzędzia systemowe co raz częściej pisze się w innych językach. Z takim rozwiązaniem trzeba tylko pamiętać, że jak coś zapiszesz w tym kawałku pamięci to się od razu zapisze, możesz tego nie chcieć (no ale memcpy() nie boli)

Nie do końca jest to prawda, zależy od implementacji - nie bez powodu mamy funkcję systemową msync:

https://man.openbsd.org/msync
https://man7.org/linux/man-pages/man2/msync.2.html.

  1. Zwracanie struktury przez wskaźnik w argumencie to anarchronizm. Jak kiedyś sprawdzałem, to GCC (z opcją -O2) nawet lepiej zoptymalizuje strukturę przekazywaną normalnie przez wartość. Sam byłem zdziwiony.

Popieram wniosek.

  1. Używaj C99/C11. Warto!

Popieram wniosek.

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