Возникли проблемы при объединении имени и фамилии - PullRequest
0 голосов
/ 19 марта 2019

Вот мой код:

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

int main(void)
{
    char firstName[10];
    char lastName[10];
    char fullName[30];
    int length, length2;
    printf("Please enter the first name: ");
    scanf("%s", firstName);
    length = strlen(firstName);
    while (length > 10)
    {
        printf("Error! First name should have < 10 characters.\n");
        printf("Please enter the first name again: ");
        scanf("%s", firstName);
        length = strlen(firstName);
    }
    printf("Please enter the last name: ");
    scanf("%s", lastName);
    length2 = strlen(lastName);
    while (length2 > 10)
    {
        printf("Error! Last name should have < 10 characters.\n");
        printf("Please enter the last name again: ");
        scanf("%s", lastName);
        length2 = strlen(lastName);
    }
    strcpy(fullName, firstName);
    strcat(fullName, " ");
    strcat(fullName, lastName);
    printf("The full name is: %s\n", fullName);
    return 0;
}

Попытка защитить пользовательский ввод от ввода имен длиннее, чем емкость символьных массивов, но при этом половина фамилии копируется в имя

Ответы [ 3 ]

1 голос
/ 19 марта 2019

strcpy() и strcat() не являются безопасными по длине.Нужно использовать strncpy() и strncat().Но если буфер недостаточно длинный, обе эти функции не заканчиваются NULL.

Простое решение для вашего случая - использовать snprintf(), который всегда будет заканчиваться NULL:

size_t bytes_needed = snprintf( fullName, sizeof( fullName ), "%s %s", firstName, lastName );
if ( bytes_needed >= sizeof( fullName ) )
{
    fprintf( stderr, "<fullName> is too small, needed %lu bytes\n", bytes_needed );
}
0 голосов
/ 19 марта 2019

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

Как упоминалось в моем первом комментарии, при использовании семейства scanf вы должны использовать модификатор field-width , чтобы scanf считывал не более size - 1 символов, чтобы сохранить место для нуль-заканчивающийся символ. В противном случае, scanf с радостью запишет за пределы вашего массива, вызвав Неопределенное поведение (что означает, что на этом этапе определенное выполнение вашего кода закончено, и ваш код может работать нормально или SegFault - или что-нибудь промежуточное)

Что дает лучший совет, какой только можно дать - Не пытайтесь читать строки ввода пользователя с помощью scanf. Вместо этого используйте fgets (или POSIX getline), чтобы прочитать всю строку за раз, а затем проанализировать все, что вам нужно из строки (или, если вы используете всю строку в качестве строки, просто обрежьте '\n' с конца) , Таким образом, то, что остается в вашем входном буфере, не зависит от используемого scanf спецификатора преобразования (который мучает новых программистов на C без конца ...)

При вводе пользовательского ввода вы всегда должны защищать пользователя от отмены ввода, вводя Ctrl + d (или Ctrl + z в окнах), чтобы создать руководство EOF. Вы делаете это, проверяя возвращение любой используемой вами функции ввода.

Если вы хотите убедиться, что пользователь дает вам действительный ввод (или отменяет его), просто поместите ваше приглашение и прочитайте его в непрерывный цикл и break; цикл, как только вход удовлетворяет всем вашим условиям.

При чтении более одного ввода в один и тот же буфер, например fullName, помогает сохранить переменную, содержащую количество символов available (или remaining). Таким образом, вместо проверки полного размера буфера для вашего второго ввода, вы проверяете оставшиеся символы в буфере, чтобы проверить, подойдет ли ваш lastName (плюс один дополнительный символ для " " (пробел) между первым / фамилия.

Чтобы убедиться, что входные данные будут соответствовать, считайте во временный буфер достаточного размера (* Не экономьте на размере буфера!), Например,

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

#define MAXN   128u     /* if you need a constant, #define one (or more) */
#define MAXC  1024u     /* max line buffer size */

int main(void)
{
    char buf[MAXC];                 /* buffer to hold input */
    char fullName[MAXN];            /* 128 chars for first/last will do */
    size_t available = MAXN - 1;    /* avaliable chars in fullName */

Выше у вас есть временный буфер 1024 символов - которого должно быть более чем достаточно, если пользовательский кошка не ложится спать на клавиатуре. Вы прочитаете в буфер символов 1024 и затем проверите, будет ли он соответствовать символам available для fullName. Комбинируя это с непрерывным циклом чтения, вы можете сделать:

    for (;;) {  /* loop continually until valid input or user cancels */
        size_t length;                  /* string funcitons use size_t */
        printf ("Please enter the first name: ");
        if (!fgets (buf, MAXC, stdin)) {    /* use fgets - validate */
            fputs ("(user canceled input)\n", stdout);
            return 0;
        }
        buf[(length = strcspn (buf, "\r\n"))] = 0;  /* trim trailing '\n' */
        if (length > available) {   /* validate length - and save length    */
            fprintf (stderr, "error: name must have < %zu characters.\n",
                    available);
        }
        else {  /* name fits - copy */
            strcpy (fullName, buf);
            available -= length;
            break;
        }

( примечание: нет необходимости в firstName или lastName, так как вы вводите оба в fullname. Просто читайте с buf и добавляйте к fullName по ходу дела)

Также обратите внимание на использование strcspn, которое возвращает количество символов в строке, НЕ содержащейся в наборе исключения "\r\n". Таким образом, он считывает количество символов в строке вплоть до первого найденного конца строки - а затем перезаписывает строку, заканчивающуюся нулем (как '\0'), чтобы завершить нулем в этой точке. Вы можете использовать strlen, но strcspn может сделать это за вас, включив набор исключений, позволяющий завершить в одной команде.

Чтение фамилии практически идентично имени, за исключением того, что когда вы собираетесь добавить фамилию к fullName, вам необходимо добавить " ", прежде чем вы это сделаете. Разница:

        else {  /* name fits, concatenate space and lastName */
            strcat (fullName, " ");
            strcat (fullName, buf);
            available -= length + 1;
            break;
        }

В целом, вы можете сделать:

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

#define MAXN   128u     /* if you need a constant, #define one (or more) */
#define MAXC  1024u     /* max line buffer size */

int main(void)
{
    char buf[MAXC];                 /* buffer to hold input */
    char fullName[MAXN];            /* 128 chars for first/last will do */
    size_t available = MAXN - 1;    /* avaliable chars in fullName */

    for (;;) {  /* loop continually until valid input or user cancels */
        size_t length;                  /* string funcitons use size_t */
        printf ("Please enter the first name: ");
        if (!fgets (buf, MAXC, stdin)) {    /* use fgets - validate */
            fputs ("(user canceled input)\n", stdout);
            return 0;
        }
        buf[(length = strcspn (buf, "\r\n"))] = 0;  /* trim trailing '\n' */
        if (length > available) {   /* validate length - and save length    */
            fprintf (stderr, "error: name must have < %zu characters.\n",
                    available);
        }
        else {  /* name fits - copy */
            strcpy (fullName, buf);
            available -= length;
            break;
        }
    }

    for (;;) {  /* ditto - above comments */
        size_t length;
        printf ("Please enter the last name : ");
        if (!fgets (buf, MAXC, stdin)) {
            fputs ("(user canceled input)\n", stdout);
            return 0;
        }
        buf[(length = strcspn (buf, "\r\n"))] = 0;
        if (length > available - 1) {   /* -1 to account for space */
            fprintf (stderr, "error: name must have < %zu characters.\n",
                    available - 1);
        }
        else {  /* name fits, concatenate space and lastName */
            strcat (fullName, " ");
            strcat (fullName, buf);
            available -= length + 1;
            break;
        }
    }

    printf ("\nThe full name is: '%s'\n", fullName);

    return 0;
}

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

$ ./bin/first_last
Please enter the first name: Samuel
Please enter the last name : Clemens

The full name is: 'Samuel Clemens'

или случай, когда пользователь отменяет ввод:

$ ./bin/first_last
Please enter the first name: (user canceled input)

Посмотрите вещи и дайте мне знать, если у вас есть дополнительные вопросы.

0 голосов
/ 19 марта 2019

Логика мне кажется хорошей, но должно быть условие if вместо цикла while.

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