Максимальная длина строки с помощью scanf - > ANSI C

Я:

#define MAX_STR_LEN 100

и я хочу поставить в scanf шаблон, чтобы я мог контролировать длину строки:

scanf("%100[^n]s",sometext)

пробовал:

scanf("%MAX_STR_LEN[^n]s",sometext)
scanf("%"MAX_STR_LEN"[^n]s",sometext)
scanf("%",MAX_STR_LEN,"[^n]s",sometext)

и это не сработало. Я просто хочу избежать переполнения буфера, так как" sometext " выделяется с malloc(MAX_STR_LEN)...

какие идеи?

5 ответов


Я не был доволен ни одним из этих решений, поэтому я исследовал дальше и обнаружил GNU GCC macro stringification

который можно использовать как:

#define XSTR(A) STR(A)
#define STR(A) #A
#define MAX_STR_LEN 100
scanf("%"XSTR(MAX_STR_LEN)"[^\n]s", sometext)

может быть, VS2010 предлагает что-то подобное?


Я просто хочу избежать переполнения буфера

тогда не использовать scanf(). Совсем.

если вы сканируете строки текста, не #define MAX_STR либо. Вы можете ХАЗ LINE_MAX на <limits.h> (если вы нацелены на системы, совместимые с POSIX):

char buf[LINE_MAX];
fgets(buf, sizeof(buf), stdin);

следует сделать трюк.


как почти все говорят, лучше использовать fgets(..., stdin) для решения этой проблемы.

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

макрос, который безопасно заменяет scanf ()

макрос, который я предложил (работа с совместимым C99 компиляторы) составляет safe_scanf(), как показано в следующая программа:

#include <stdio.h>
#define safe_scanf(fmt, maxb, ...) { \
    char buffer[maxb+1] = { [maxb - 1] = '' }; \
    fgets(buffer, maxb+1, stdin); \
    if ((buffer[maxb - 1] != '') && (buffer[maxb - 1] != '\n')) \
        while(getchar() != '\n') \
           ; \
    sscanf(buffer, fmt, __VA_ARGS__); \
  }

#define MAXBUFF 20     

int main(void) {
   int x; float f;      
   safe_scanf("%d %g", MAXBUFF+1, &x, &f);
   printf("Your input was: x == %d\t\t f == %g",  x, f);
   return 0;
}  

вы должны tune значение MAXBUFF согласно вашим потребностям...
Хотя макрос safe_scanf() довольно твердые,
есть некоторые слабости в использовании макро-подхода:
Отсутствует тип-проверка параметров, отсутствуют значения " return "(которые практически не отличаются от" true"scanf() функция, которая возвращает int, ценную информацию для проверки ошибок), и так далее.
Все это проблемы имеют решение, но это часть другой темы...

возможно, Наиболее точным решением является определение функции my_scanf() С переменным количеством параметров, вызывая stdarg.h библиотека, совместная с комбинацией fgets() и vsscanf(). Здесь у вас есть код:

#include <stdio.h>
#include <stdarg.h>

int my_scanf(const char* fmt, const unsigned int maxbuff, ...) {
    va_list ptr;
    int ret;

    if (maxbuff <= 0)
       return EOF; /* Bad size for buffer[] */

    char buffer[maxbuff+1];
    buffer[maxbuff-1] = '';  /* Quick buffer cleaning... */

    if (fgets(buffer, maxbuff+1, stdin) == NULL)
       return EOF; /* Error detected */
    else {
        if ((buffer[maxbuff-1] != '\n') && (buffer[maxbuff-1] != ''))
            /* Condition logically equivalent to:
                   fgets() has not reached an '\n'
            */
            while (getchar() != '\n')
               ; /* "Flushing" stdin... */

        va_start(ptr, maxbuff);
        ret = vsscanf(buffer, fmt, ptr);
        va_end(ptr);
        return ret;
    }    
}

#define MAXBUFF 20
int main(void) {
   int x; 
   float z;
   int scanf_ret = my_scanf("%d %g", MAXBUFF, &x, &z);
   printf("\nTest:\n x == %d\n z == %g\n scanfret == %d", x, z, scanf_ret);
   getchar();   
   return 0;   
}

функция my_scanf () имеет прототип

int my_scanf(const char* fmt, const int maxbuff, ...);

он принимает строку формата fmt который ведет себя так же, как и любой другой scanf()-как делает.
2-й параметр-максимальное число chars это будет эффективно принято со стандартного ввода (клавиатуры).
Возвращаемое значение -int, которая составляет EOF если maxbuff не имеет смысла, или произошла ошибка ввода. Если возвращается неотрицательное значение, это то же самое, что было бы возвращено стандартными функциями sscanf() или vsscanf().

внутри функции, maxbuff увеличивается на 1, потому что fgets() освобождает место для дополнительного символа "\0".
Непозитивные значения maxbuff немедленно выбросить.
fgets() будет читать строку читается из stdin (клавиатура) с номер для maxbuff символы, включая '\n'.
Если пользователь ввел очень длинную строку, то она будет усечена, и необходим какой-то "флеш" механизм для того, чтобы отбросить все символы к следующему '\n' ( ENTER). Если нет, следующая клавиатура чтение может иметь более старые иероглифы, что совершенно нежелательно.
Условие для "промывки" заключается в том, что fgets() не достиг "\n " после чтения stdin.
Это так, если и только если buffer[maxbuff - 1] не равно '\0 ' или '\n'.
(Проверь!)
Наконец, подходящая комбинация stdarg.h макрос и используется для обработки списка переменных параметров.


рекомендую fgets(buffer, sizeof(buffer), stdin) подход.

если вы еще хотите использовать scanf () вы можете создать его формат во время выполнения.

#define MAX_STR_LEN 100
char format[2 + sizeof(size_t)*3 + 4 + 1];  // Ugly magic #
sprintf(format, " %%%zu[^\n]", (size_t) MAX_STR_LEN);
scanf(format, sometext);

или переопределить MAX_STR_LEN как строку

#define MAX_STR_LEN "100"
scanf(" %" MAX_STR_LEN "[^\n]", sometext);

еще рекомендую fgets().
Примечание fgets() поставит лидирующие пробелы и трейлинг \n в вашем буфере тогда как " %[^\n]" не будет.
BTW: трейлинг s в ваших форматах, скорее всего, не делает то, что вы думаете.


как о

scanf("%.*[^\n]s", MAX_STR_LEN, sometext)