Проверяется 2 printf()
строки формата совместимы это упражнение в формате разборе.
C, по крайней мере, не имеет стандартное время выполнения не сравнивать функции, такие как:
int format_cmp(const char *f1, const char *f2); // Does not exist
форматы, такие как "%d %f"
и "%i %e"
совместимы, очевидно, в том, как ожидают int
и float/double
. Примечание: float
- double
, так как short
и signed char
- int
.
Форматы "%*.*f"
и "%i %d %e"
совместимы, но не очевидно: и ожидать int
, int
и float/double
.
Форматы "%hhd"
и "%d"
и ожидать int
, даже несмотря на то, первый будет иметь его значения для литых signed char
перед печатью.
Форматы "%d"
и "%u"
являются не не совместимы. Хотя многие системы будут вести себя так, как надеялись. Примечание: Обычно char
будет продвигать до int
.
Форматы "%d"
и "%ld"
являются не строго совместим. В 32-битной системе есть эквивалент, но не в общем. Конечно, код может быть изменен для этого. OTOH "%lf"
и "%f"
являются совместимыми из-за обычных рекламных акций до float
до double
.
Форматы "%lu"
и "zu"
могут быть совместимы, но это зависит от реализации unsigned long
и size_t
. Дополнения к коду могут позволить это или связанные эквивалентности.
Некоторые комбинации модификаторов и спецификаторов не определены как "zp"
. Следующее не допускает подобных эзотерических комбинаций, но сравнивает их.
Модификаторы, такие как "$"
, являются расширениями стандарта C и не реализованы в следующем.
Тест на совместимость для printf()
отличается от scanf()
.
#include <ctype.h>
#include <limits.h>
#include <stdio.h>
#include <string.h>
typedef enum {
type_none,
type_int,
type_unsigned,
type_float,
type_charpointer,
type_voidpointer,
type_intpointer,
type_unknown,
type_type_N = 0xFFFFFF
} type_type;
typedef struct {
const char *format;
int int_queue;
type_type type;
} format_T;
static void format_init(format_T *state, const char *format);
static type_type format_get(format_T *state);
static void format_next(format_T *state);
void format_init(format_T *state, const char *format) {
state->format = format;
state->int_queue = 0;
state->type = type_none;
format_next(state);
}
type_type format_get(format_T *state) {
if (state->int_queue > 0) {
return type_int;
}
return state->type;
}
const char *seek_flag(const char *format) {
while (strchr("-+ #0", *format) != NULL)
format++;
return format;
}
const char *seek_width(const char *format, int *int_queue) {
*int_queue = 0;
if (*format == '*') {
format++;
(*int_queue)++;
} else {
while (isdigit((unsigned char) *format))
format++;
}
if (*format == '.') {
if (*format == '*') {
format++;
(*int_queue)++;
} else {
while (isdigit((unsigned char) *format))
format++;
}
}
return format;
}
const char *seek_mod(const char *format, int *mod) {
*mod = 0;
if (format[0] == 'h' && format[1] == 'h') {
format += 2;
} else if (format[0] == 'l' && format[1] == 'l') {
*mod = ('l' << CHAR_BIT) + 'l';
format += 2;
} else if (strchr("ljztL", *format)) {
*mod = *format;
format++;
} else if (strchr("h", *format)) {
format++;
}
return format;
}
const char *seek_specifier(const char *format, int mod, type_type *type) {
if (strchr("di", *format)) {
*type = type_int;
format++;
} else if (strchr("ouxX", *format)) {
*type = type_unsigned;
format++;
} else if (strchr("fFeEgGaA", *format)) {
if (mod == 'l') mod = 0;
*type = type_float;
format++;
} else if (strchr("c", *format)) {
*type = type_int;
format++;
} else if (strchr("s", *format)) {
*type = type_charpointer;
format++;
} else if (strchr("p", *format)) {
*type = type_voidpointer;
format++;
} else if (strchr("n", *format)) {
*type = type_intpointer;
format++;
} else {
*type = type_unknown;
exit(1);
}
*type |= mod << CHAR_BIT; // Bring in modifier
return format;
}
void format_next(format_T *state) {
if (state->int_queue > 0) {
state->int_queue--;
return;
}
while (*state->format) {
if (state->format[0] == '%') {
state->format++;
if (state->format[0] == '%') {
state->format++;
continue;
}
state->format = seek_flag(state->format);
state->format = seek_width(state->format, &state->int_queue);
int mod;
state->format = seek_mod(state->format, &mod);
state->format = seek_specifier(state->format, mod, &state->type);
return;
} else {
state->format++;
}
}
state->type = type_none;
}
// 0 Compatible
// 1 Not Compatible
// 2 Not Comparable
int format_cmp(const char *f1, const char *f2) {
format_T state1;
format_init(&state1, f1);
format_T state2;
format_init(&state2, f2);
while (format_get(&state1) == format_get(&state2)) {
if (format_get(&state1) == type_none)
return 0;
if (format_get(&state1) == type_unknown)
return 2;
format_next(&state1);
format_next(&state2);
}
if (format_get(&state1) == type_unknown)
return 2;
if (format_get(&state2) == type_unknown)
return 2;
return 1;
}
Примечание: только минимальное тестирование выполнено. Можно было бы добавить много дополнительных соображений.
Известные недостатки: hh,h,l,ll,j,z,t
модификаторы с n
. l
с s,c
.
[изменить]
ОП комментарии относительно проблем безопасности. Это изменяет характер сообщения и сравнивает его с безопасным. Я бы предположил, что один из шаблонов (A) будет эталонным шаблоном, а следующий (B) будет тестом. Тест будет «В, по крайней мере, такой же безопасный, как A?». Пример A = "%.20s"
и B1 = "%.19s"
, B2 = "%.20s"
, B3 = "%.21s"
. B1
и B2
проходят тест безопасности, поскольку они не извлекают больше 20 char
. B3
- проблема, поскольку она проходит через контрольный предел 20 char
. Далее любой, не имеющий ширины с %s %[ %c
, является проблемой безопасности - в контрольной или тестовой схеме. Этот код ответа не решает эту проблему.
Как уже упоминалось, код еще не обрабатывает модификаторы с "%n"
.
[2018 редактировать]
Что касается «Форматы "%d"
и "%u"
не совместимы.»: Это для значения должны быть напечатаны в целом. Для значений в диапазоне [0..INT_MAX]
любой формат может работать на C11dr §6.5.2.2 6.
Возможно, вы сможете сгибать 'NS_FORMAT_FUNCTION' по своему желанию. [Проверьте этот ответ SO] (http://stackoverflow.com/a/16983220/1292061), а также [Документы Clang для '__format__'] (http://clang.llvm.org/docs/AttributeReference.html # формат-гну-формат). – ravron
Или посмотрите здесь GNU: http://www.gnu.org/software/libc/manual/html_node/Parsing-a-Template-String.html –
'parse_printf_format' выглядит здорово. Как импортировать его? –