Считывание имени файла с использованием «fgets» и проверка, если длина строки длиннее 20 символов, заставляет пользователя повторно вводить - PullRequest
0 голосов
/ 10 июля 2019

Я пытаюсь получить ввод от пользователя на stdin, используя fgets(). Если пользователь вводит более 20 символов, я прочитал его снова, используя fgets(). Но во второй раз он просто не читает, а повторяет оператор print до fgets и переходит к приведенному ниже коду. Я попытался очистить буфер, но он не работает.

Вся программа читает из файла после проверки, а затем переворачивает биты и записывает их в другой файл. Эта часть отлично работает. У меня есть проверка цикла, если файл не существует, и я прошу пользователя войти снова, используя fgets, который тоже отлично работает.

FILE *fp,*fp1;
char name[20];

printf("File name please\n");
fgets(name,200,stdin);
while (strlen(name) > 19) {
  name[0] = '\0';
  printf ("Too long please change\n");
  fgets(name,200,stdin);
}

// posting the other check statement that works fine
while (fp == NULL){
  printf("Re-enter the file name\n");
  fgets(name,2000,stdin);
  for (i = 0;i < 200; i++){
    if (name[i] == '\n'){
      name[i] = '\0';
      break;
    }
  }

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

1 Ответ

1 голос
/ 10 июля 2019

Ваш код вызывает Неопределенное поведение , если пользователь вводит более 18-characters и нажимает Введите .Зачем?Вы объявляете char name[20];, что означает, что существует только хранилище для 20-characters всего (включая нуль-завершающий символ).Тем не менее, вы тогда скажете fgets прочитать до 200 символов с fgets(name,200,stdin);.

fgets будет делать то, что вы сказали.Если ваш пользователь введет 19-characters, fgets попытается записать 21-characters в name, записав за пределы name и вызвав Неопределенное поведение .

Почему 21-characters, если пользователь вводит только 19-characters?

Вспомните, что происходит, когда пользователь нажимает Ввод , указывая конец ввода.fgets (фактически все строковые входные функции, включая POSIX getline) будут включать '\n' в заполненный буфер (при условии, что достаточно места) - и вы сказали fgets Есть 200-characters доступны.Тем не менее, name имеет всего 20, например,

        20 characters total

 first          +---+---+---+---+---+
   15       ... | . | . | x | \n| \0|
 characters     +---+---+---+---+---+

. Чтобы решить вашу проблему, вы всегда должны убедиться, что ваше хранилище равно или превышает количество символов, которое выскажи fgets читать.Как правило, не экономит на размере буфера .Если вы планируете читать 20-characters как name, тогда предоставьте буфер, по крайней мере, вдвое длиннее самого длинного имени, которое вы можете найти.Я бы предпочел, чтобы длина была 10 000 байт, а длина - 1 байт ...

Чтобы получить ввод, вы управляете циклом ввода с помощью возврата функции ввода (либо в условном цикле, либо какусловие внутри цикла), которое будет обрабатывать EOF, пустой ввод, а также слишком длинную длину.

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

#define MAXC 256    /* if you need a constant, #define one (or more) */
...
    for (;;) {  /* loop continually until good input or user cancels */
        fputs ("enter name: ", stdout);     /* prompt */
        if (!fgets (name, MAXC, stdin)) {   /* handle manual EOF */
            fputs ("(user canceled)\n", stdout);
            return 1;
        }
        ...
    }

В цикле вы просто проверяете длину name с помощью strlen и обрезаете '\n' изконец буфера.Если длина превышает 19-characters, просто обработайте ошибку и continue запросите другую запись.

В целом, вы можете сделать что-то похожее на:

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

#define MAXC 256    /* if you need a constant, #define one (or more) */
#define MAXN  20

int main (void) {

    char name[MAXC] = {0};
    size_t len = 0;

    for (;;) {  /* loop continually until good input or user cancels */
        fputs ("enter name: ", stdout);     /* prompt */
        if (!fgets (name, MAXC, stdin)) {   /* handle manual EOF */
            fputs ("(user canceled)\n", stdout);
            return 1;
        }
        len = strlen (name);                /* get length of name */
        if (len > MAXN || *name == '\n') {  /* exceeds length or empty */
            fputs ("  error: name exceeds 20 char or empty\n", stderr);
            continue;
        }
        else {      /* good name, trim \n, break loop */
            if (len && name[len - 1] == '\n')   /* check last char '\n' */
                name[--len] = 0;                /* overwrite '\n' with \0 */
            break;
        }
    }

    printf ("\nname: %s  (%zu chars)\n", name, len);

    return 0;
}

( примечание: выше, если пользователь генерирует руководство EOF с Ctrl + d или Ctrl + z на окнах, чтение останавливается и программа завершается. Если длинапревышает желаемую длину, или если пользователь просто нажимает Enter , что приводит к буферу, содержащему только '\n' [из пустой строки ниже], генерируется ошибка, и пользователь просит повторно ввести имя.)

Пример использования / Вывод

При выполнении процедуры ввода вводится 20 символов, затем Ввод только и, наконец, действительный 19-строка символов.

$ ./bin/namelen
enter name: 12345678901234567890
  error: name exceeds 20 char or empty
enter name: 
  error: name exceeds 20 char or empty
enter name: 1234567890123456789

name: 1234567890123456789  (19 chars)

Вы можете отрегулировать длину по своему усмотрению.

Если пользователь отменяет действие в любой точке, перед выходом отображается следующее:

$ ./bin/namelen
enter name: (user canceled)

В качестве альтернативы вызову strlen, а затем вручную проверьте, включен ли '\n'В буфере вы можете использовать strcspn, чтобы получить длину и обрезать конечный '\n', например:

    for (;;) {  /* loop continually until good input or user cancels */
        fputs ("enter name: ", stdout);     /* prompt */
        if (!fgets (name, MAXC, stdin)) {   /* handle manual EOF */
            fputs ("(user canceled)\n", stdout);
            return 1;
        }
        name[(len = strcspn (name, "\n"))] = 0; /* save len, trim '\n' */
        if (len > MAXN - 1 || !*name) {         /* exceeds length/empty */
            fputs ("  error: name exceeds 20 char or empty\n", stderr);
            continue;
        }
        else        /* good name, break loop */
            break;
    }

Дайте мне знать, если у вас есть вопросы.Правило большого пальца - Не экономьте на размере буфера!

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...