prüfen 2 printf()
Formatstrings kompatibel sind, ist eine Übung im Format Parsing.
C, zumindest hat keine Standard-Laufzeitfunktion vergleichen, wie zum Beispiel:
int format_cmp(const char *f1, const char *f2); // Does not exist
Formate wie "%d %f"
und "%i %e"
sind natürlich kompatibel, dass sowohl ein int
und float/double
erwarten. Hinweis: float
werden zu double
befördert, da short
und signed char
zu int
befördert werden.
Formate "%*.*f"
und "%i %d %e"
kompatibel sind, aber nicht auf der Hand: Beide erwarten ein int
, int
und float/double
.
Formate "%hhd"
und "%d"
erwarten sowohl eine int
, auch wenn die erste wird es haben cast signed char
vor dem Drucken Werte.
Die Formate "%d"
und "%u"
sind nicht kompatibel. Auch wenn sich viele Systeme wie erhofft verhalten werden. Hinweis: Normalerweise wird char
zu int
promoten.
Die Formate "%d"
und "%ld"
sind nicht streng kompatibel. Auf einem 32-Bit-System gibt es gleichwertige, aber nicht allgemein. Natürlich kann der Code geändert werden, um dies zu berücksichtigen. OTOH "%lf"
und "%f"
sind kompatibel aufgrund der üblichen Argument Promotionen von float
bis double
.
Formate "%lu"
und "zu"
können kompatibel sein, aber das hängt von der Implementierung von unsigned long
und size_t
. Ergänzungen zum Code könnten diese oder ähnliche Äquivalenzen ermöglichen.
Einige Kombinationen von Modifikatoren und Spezifizierer sind nicht definiert wie "zp"
. Das Folgende erlaubt solche esoterischen Kombinationen nicht - vergleicht sie aber.
Modifikatoren wie "$"
sind Erweiterungen zu Standard C und werden im Folgenden nicht implementiert. Der Kompatibilitätstest für printf()
unterscheidet sich von 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;
}
Hinweis: nur minimale Tests durchgeführt. Viele zusätzliche Überlegungen könnten hinzugefügt werden.
Bekannte Mängel: hh,h,l,ll,j,z,t
Modifikatoren mit n
. l
mit s,c
.
[Bearbeiten]
OP Kommentare zu Sicherheitsbedenken. Dies ändert die Art des Posts und des Vergleichs von einer Gleichheit zu einer Sicherheitsstellung. Ich würde mir vorstellen, dass eines der Muster (A) ein Referenzmuster und das nächste (B) der Test wäre. Der Test wäre "ist B mindestens so sicher wie A?". Beispiel A = "%.20s"
und B1 = "%.19s"
, B2 = "%.20s"
, B3 = "%.21s"
. B1
und B2
beide den Sicherheitstest bestehen, da sie nicht mehr die 20 char
extrahieren. B3
ist ein Problem, da es die Referenzgrenze von 20 char
überschreitet. Weiter beliebig nicht-Breite qualifiziert mit %s %[ %c
ist ein Sicherheitsproblem - in der Referenz oder Testmuster. Der Code dieser Antwort behebt dieses Problem nicht.
Wie bereits erwähnt, verarbeitet Code noch keine Modifizierer mit "%n"
.
[2018 bearbeiten]
In Bezug auf "Formate "%d"
und "%u"
sind nicht kompatibel.": Dies ist für Werte in der Regel gedruckt werden. Für Werte im Bereich [0..INT_MAX]
kann jedes Format nach C11dr funktionieren. §6.5.2.2 6.
Sie können möglicherweise 'NS_FORMAT_FUNCTION' zu Ihrem Willen verbiegen. [Überprüfen Sie diese SO-Antwort] (http://stackoverflow.com/a/16983220/1292061), sowie die [Clang-Dokumentation für '__format__'] (http://clang.llvm.org/docs/AttributeReference.html # Format-Gnu-Format). – ravron
Oder schauen Sie hier nach GNU: http://www.gnu.org/software/libc/manual/html_node/Parsing-a-Template-String.html –
'parse_printf_format' sieht cool aus. Wie importiere ich es? –