Максимальная длина строки с помощью 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
в ваших форматах, скорее всего, не делает то, что вы думаете.