0

Program should read list of filenames, open these files and put their handles in the array of structure, then read strings and print consecutive lines of strings to smallest files by using handles contained in array of structures.

My program puts data from all lines to only one file which is initially the smallest which is false because it should the one which is smallest with every time it prints data into the file. This is my program:

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

struct file_t
{
 FILE* f;
 int size;
}t[5];
void close_file(struct file_t* f) {
 if (f == NULL || f->f == NULL) {

 }
 else {
     fclose(f->f);
 }
}
int open_file(struct file_t* f, const char* filename) {
 if (f == NULL || filename == NULL) {
     return 1;
 }
 FILE* fp;
 fp = fopen(filename, "ab");
 if (fp == NULL) {
     return 2;
 }
 long int res = ftell(fp);
 fclose(fp);
 f->size = res;
 f->f = fopen(filename, "ab+");
 if (fp == NULL) {
     return 2;
 }
 return 0;
}
struct file_t* find_min(const struct file_t* files, int size) {
 if (files == NULL || size <= 0) {
     return NULL;
 }
 int x = (files + 0)->size, i = 0, index = 0;
 for (i = 0; i < size; i++) {
     if ((files + i)->size <= x) {
         x = (files + i)->size;
         index = i;
     }
 }
 return (struct file_t*)(files + index);
}
int main() {
 puts("Input files' names:");
 char tab[100];
 int num = 0;
 while(1==1){
     if(fgets(tab, 100, stdin)==NULL||*tab=='\n'){
         if (num == 0) {
             printf("Couldn't open file");
             return 4;
         }
         break;
     }
     int index=strlen(tab);
     *(tab+index-1)='\x0';
     if (strlen(tab) > 30) {
         *(tab + 30) = '\x0';
     }
     if (open_file((t + num), tab) > 0) {
     }
     else {
         num++;
     }
 }
 if (num == 0) {
     printf("Couldn't open file");
     return 4;
 }
 char str[1000];
 printf("Input text:");
 *str = '\x0';
 while (fgets(str, 1000, stdin)==NULL||*str!='\n') {
     int index=strlen(str);
     *(str+index-1)='\x0';
     struct file_t* p = find_min(t, num);
     fwrite(str, sizeof(char), strlen(str), p->f);
 }
 for (int i = 0; i < num; i++) {
     close_file(t + i);
 }
 printf("File saved");
 return 0;
}



Brian Tompsett - 汤莱恩
  • 5,753
  • 72
  • 57
  • 129
  • After posting question, the community will be trying to solve your issue. You cant update the question in between. If you want to do. Mention an EDIT and do what ever you want. Now my answer just makes no sense to the community ! – m0hithreddy May 28 '20 at 09:29

2 Answers2

0

Where are you updating the file_t->size when you write into a file? You are calling this:

fwrite(str, sizeof(char), strlen(str), p->f);

But after that you should do p->size += strlen(str) to update its size, otherwise all file sizes are set to initial values, and hence all strings get written to a single file.

As for getting garbage data, try printing the string you are reading from scanf in the while loop.

You are using scanf to read characters until '\n', but you are not reading the '\n' itself. You need a fseek(stdin, 0, SEEK_END); in that loop as well.

Finally, why are you using syntax like this:

(files + i)->size

When you can call it more cleanly like this:

files[i].size

You code is really hard to read because of this.

Catch22
  • 391
  • 2
  • 5
0

There are some critical bugs that you need to resolve.

As a matter of fact even fflush() won't work. fflush is something that is designed for flushing output streams, and its behavior with input streams is implementation-dependent. Please refer to this link for more details stdinflush

  • scanf("%[^\n]s", tab)

If you are using this in a loop or multiple times, only the first read will succeed. The reason being, the \n character is left out from the previous input, and as said earlier fflush() might not be successful in removing that \n. The further calls to scanf() will simply return without reading anything.

  • '\0x' If you are intending to use this as string terminator then this is not it. It is a multi-character constant with an integer value 120. Below is a vague test run

Code

#include <stdio.h>

int main()
{
        if ('\0' == '\0x' )
                printf("both are same\n");

        printf("%d",'\0x');
}

Compilation Warnings

test.c: In function ‘main’:
test.c:5:14: warning: multi-character character constant [-Wmultichar]
    5 |  if ('\0' == '\0x' )
      |              ^~~~~
test.c:8:14: warning: multi-character character constant [-Wmultichar]
    8 |  printf("%d",'\0x');
      |              ^~~~~

Output

120

1) You should compute the file size every time in find_min() as it gets changed whenever you write data to the file.

2) fwrite()won't actually dump the data to file immediately. you need to call fflush().

After resolving the above issues, this is the modified code.

Code

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include <stdlib.h>
#include <math.h>
#include <sys/stat.h>

struct file_t
{
    FILE* f;
    int size;
}t[5];

void close_file(struct file_t* f) {
    if (f == NULL || f->f == NULL) {

    }
    else {
        fclose(f->f);
    }
}

int open_file(struct file_t* f, const char* filename) {
    if (f == NULL || filename == NULL) {
        return 1;
    }

    f->f = fopen(filename, "a");

    if (f->f == NULL)
        return 2;

    struct stat statbuf;
    fstat(fileno(f->f), &statbuf);
    f->size = statbuf.st_size;
    return 0;
}

struct file_t* find_min(const struct file_t* files, int size) {
    if (files == NULL || size <= 0) {
        return NULL;
    }
    struct stat statbuf;

    fstat(fileno(files->f), &statbuf);

    int x = statbuf.st_size, i = 0, index = 0;
    for (i = 0; i < size; i++) {
        fstat(fileno((files+i)->f), &statbuf);

        if (statbuf.st_size < x) {
            x = statbuf.st_size;
            index = i;
        }
    }
    return (struct file_t*)(files + index);
}

int main() {
    puts("Input files' names:");
    char tab[100];
    int num = 0;

    while(1){

        int c;
        while (1) {
            c = getc(stdin);

            if (c == EOF || c == ' ')
                goto user_input;

            if(c != '\n')
                break;
        }
        tab[0] = c;

        if (scanf("%[^\n]s", tab+1) == EOF)
            break;

        if (*tab == '\0') {
            if (num == 0) {
                printf("Couldn't open file");
                return 4;
            }
            break;
        }
        if (strlen(tab) > 30) {
            *(tab + 30) = '\0';
        }
        if (open_file((t + num), tab) > 0) {
        }
        else {
            num++;
        }
        *tab = '\0';
    }

    user_input:

    if (num == 0) {
        printf("Couldn't open file");
        return 4;
    }
    fflush(stdin);
    char str[1000];
    printf("Input text:\n");
    *str = '\0';

    while(1) {

        int c;
        while(1) {
            c = getc(stdin);

            if (c == EOF)
                goto main_exit;

            if (c != '\n')
                break;
        }
        str[0] = c;

        if (scanf("%[^\n]s", str+1) == EOF)
             break;

        struct file_t* p = find_min(t, num);
        fwrite(str, sizeof(char), strlen(str), p->f);
        fflush(p->f);
    }

    main_exit:
    for (int i = 0; i < num; i++) {
        close_file(t + i);
    }

    printf("File saved");
    return 0;
}

Terminal Session

$ ./a.out
Input files' names:
test file1.txt
test file2.txt
' '(NOTE: Space character inputted before pressing enter.)
Input text:
this is 
stackoverflow
File saved

test file1.txt

this is 

test file2.txt

stackoverflow

Note for breaking from the first loop (Files input). You need to enter space and then press enter (You can tweak around this).

m0hithreddy
  • 1,752
  • 1
  • 10
  • 17