Ошибка сегментации вместо отображения сообщения - чтение из файла с помощью указателей в c - PullRequest
1 голос
/ 19 апреля 2020

Я написал программу, которая читает из файла. Я использую условие, при котором я печатаю, что массив слишком большой, но когда я вместо этого показываю это сообщение, у меня возникает ошибка сегментации.

Это моя программа

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

#define N 10000 // Maximum array size

int _strlen(char *array) {
    int i;
    for (i = 0; array[i] != '\0'; ++i);    
    return i;
}

int readText(FILE *wp, char *s, int max) {
    int sum = 0;
    if (_strlen(s) > max) {
        printf("This array is too big. Maximum size is %d", max);
    } else {
        while ((*s++ = fgetc(wp)) != EOF) {
            sum++;
        }
        *(s-1) = '\0';
    }
    return sum;
}

int main(int argc, char *argv[]) {
    FILE *wz, *wc;                       
    char *s;
    char array[N];
    s = array;
    if (argc != 3) {                              
        printf("Wrong arguments number\n");
        printf("I should run this way:\n");
        printf("%s source result\n",argv[0]);
        exit(1);
    }

    if ((wz = fopen(argv[1], "r")) == NULL) {
        printf("Open error %s\n", argv[1]);
        exit(1);
    }
    if ((wc = fopen(argv[2], "w")) == NULL) {
        printf("Open error %s\n", argv[2]);
        exit(2);
    }

    fprintf(wc, "Read text from file source.txt");

    readText(wz, s, 10000);   

    return 0;
}

На выходе я хочу иметь: This array is too big. Maximum size is %d Вместо Segmentation fault core dumped

Кроме того, я хочу сказать, что программа это когда я использую меньший массив, но я хочу показать пользователю правильное сообщение, когда он использует слишком большой массив вместо ошибки сегментации.

Спасибо, я изменяю свою программу таким образом. Единственная проблема заключается в том, что эта программа проверяет условие if при каждом l oop, чтобы эта программа могла работать медленно.

int readText(FILE *wp, char *s, int max) {
    int sum = 0;
    if (_strlen(s) > max) {
        printf("This array is too big. Maximum size is %d", max);
    } else {
        while ((*s++ = fgetc(wp)) != EOF) {
            sum++;
            if (sum > max) {
                printf("This array is too big. Maximum size is %d", max);
                break;
            }
        }
        *(s-1) = '\0';
    }
    return sum;
}

Ответы [ 2 ]

2 голосов
/ 20 апреля 2020

Замечания / другой ответ решают ваше неопределенное поведение (ошибка сегментации в вашем случае).

Единственная проблема состоит в том, что эта программа проверяет условие if в каждом времени l oop, поэтому эта программа может быть медленным.

Ваша программа работает не медленно из-за 'если', а потому что вы читаете файл char за символ.

Используя stat или эквивалентный Функция, вы можете получить размер файла, чтобы прочитать его бросить только один Фред :

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

#define N 10000 // Maximum array size

int main(int argc, char *argv[]) {
  char array[N];
  FILE *wz, *wc;                       
  struct stat st;
  off_t sz;

  if (argc != 3) {                              
    printf("Wrong arguments number\n"
           "I should run this way:\n"
           "%s source result\n", argv[0]);
    exit(1);
  }

  if ((wz = fopen(argv[1], "r")) == NULL) {
    printf("Cannot open %s to read : %s\n", argv[1], strerror(errno));
    exit(1);
  }

  if (stat(argv[1], &st) == -1) {
    printf("Cannot get stat of %s : %s\n", argv[1], strerror(errno));
    exit(1);
  }

  if (st.st_size > N-1) {
    printf("This array is too big. Maximum size is %d", N-1);
    sz = N-1;
  }
  else
    sz = st.st_size;

  if (fread(array, 1, sz, wz) != sz)  {
    printf("cannot read %s : %s", argv[1], strerror(errno));
    fclose(wz); /* for valgrind end test etc */
    exit(1);
  }
  array[sz] = 0;
  fclose(wz);

  if ((wc = fopen(argv[2], "w")) == NULL) {
    printf("Cannot open %s to write : %s\n", argv[2], strerror(errno));
    fclose(wz); /* for valgrind end test etc */
    exit(2);
  }

  /* ... */

  fclose(wc);

  return 0;
}

Зная размер файла позволяет снять это ограничение на постоянный размер и попытаться прочитайте файл, в то время как вы можете выделить достаточно памяти для:

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

int main(int argc, char *argv[]) {
  char * array;
  FILE *wz, *wc;                       
  struct stat st;

  if (argc != 3) {                              
    printf("Wrong arguments number\n"
           "I should run this way:\n"
           "%s source result\n", argv[0]);
    exit(1);
  }

  if ((wz = fopen(argv[1], "r")) == NULL) {
    printf("Cannot open %s to read : %s\n", argv[1], strerror(errno));
    exit(1);
  }

  if (stat(argv[1], &st) == -1) {
    printf("Cannot get stat of %s : %s\n", argv[1], strerror(errno));
    exit(2);
  }

  if ((array = malloc(st.st_size + 1)) == NULL) {
    printf("Not enough memory to memorize the file %s\n", argv[1]);
    exit(3);
  }

  if (fread(array, 1, st.st_size, wz) != st.st_size)  {
    printf("cannot read %s : %s", argv[1], strerror(errno));
    fclose(wz); /* for valgrind end test etc */
    free(array); /* for valgrind etc */
    exit(4);
  }
  array[st.st_size] = 0;
  fclose(wz);

  if ((wc = fopen(argv[2], "w")) == NULL) {
    printf("Cannot open %s to write : %s\n", argv[2], strerror(errno));
    free(array); /* for valgrind etc */
    exit(5);
  }

  /* ... */

  fclose(wc);
  free(array); /* for valgrind etc */

  return 0;
}

В любом случае из-за использования программы «исходный результат» может потребоваться скопировать файл, указанный в argv [1] в файле, заданном argv [2] , в этом случае лучше читать и записывать блок за блоком, чем читать все, чтобы не использовать много памяти впустую и управлять случаем ввода размер файла grea больше, чем объем памяти.

0 голосов
/ 19 апреля 2020

Вы не можете измерить длину целевого массива с помощью _strlen(s), размер указан в качестве аргумента, а чтение неинициализированного массива с _strlen() имеет неопределенное поведение.

Более того Вы сохраняете fgetc(fp) до *s++ перед тестированием на EOF. Это неверно во всех случаях:

  • , если тип char подписан, EOF нельзя отличить от действительного значения байта \377.
  • , если char без знака, EOF не может быть протестирован, потому что он был преобразован как char значение 0xff, следовательно, l oop работает вечно, записывая после конца целевого массива, пока это не вызовет cra sh .

Вы просто хотите добавить тест в чтение l oop, чтобы остановить чтение байтов из файла, когда буфер заполнен, и прочитать байты в переменную int, чтобы вы могли проверить для надежного завершения файла.

Вот модифицированная версия:

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

#define N 10000 // Maximum array size

int readText(FILE *wp, char *s, int max) {
    int i = 0, c;
    while (i < max - 1 && (c = fgetc(wp)) != EOF) {
        s[i++] = c;
    }
    s[i] = '\0';
    return i;
}

int main(int argc, char *argv[]) {
    FILE *wz, *wc;                       
    char array[N];
    int nread;

    if (argc != 3) {                              
        printf("Wrong arguments number\n");
        printf("I should run this way:\n");
        printf("%s source result\n", argv[0]);
        exit(1);
    }

    if ((wz = fopen(argv[1], "r")) == NULL) {
        printf("Open error %s\n", argv[1]);
        exit(1);
    }
    if ((wc = fopen(argv[2], "w")) == NULL) {
        printf("Open error %s\n", argv[2]);
        exit(2);
    }

    fprintf(wc, "Read text from file source.txt\n");

    nread = readText(wz, array, N);   

    printf("Read %d bytes\n", nread);

    return 0;
}
...