2015-03-09 11 views
39

Beispiele:Wie überprüft man, ob zwei Formatzeichenfolgen kompatibel sind?

"Something %d"  and "Something else %d"  // Compatible 
"Something %d"  and "Something else %f"  // Not Compatible 
"Something %d"  and "Something %d else %d" // Not Compatible 
"Something %d and %f" and "Something %2$f and %1$d" // Compatible 

dachte ich, es sollte eine gewisse C-Funktion für das sein, aber ich bin keinen relevanten Treffer zu bekommen. Ich meine, der Compiler überprüft, dass die Formatzeichenfolge und die Argumente übereinstimmen, daher ist der Code zum Überprüfen bereits geschrieben. Die einzige Frage ist, wie ich es nennen kann.

Ich benutze Objective-C, wenn es also eine Objective-C-spezifische Lösung gibt, ist das auch in Ordnung.

+1

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

+0

Oder schauen Sie hier nach GNU: http://www.gnu.org/software/libc/manual/html_node/Parsing-a-Template-String.html –

+0

'parse_printf_format' sieht cool aus. Wie importiere ich es? –

Antwort

-1

Mein Verständnis von dem, was Sie wollen, ist, dass Sie im Grunde eine Methode wollen, die zwei Strings betrachten und erkennen kann, ob sie beide die gleichen Arten von Werten in ihnen haben. Oder etwas lange diese Zeilen .... Wenn ja, dann versuchen, diese (oder etwas entlang der Linien von dieser):

-(int)checkCompatible:(NSString *)string_1 :(NSString *)string_2 { 

    // Separate the string into single elements. 
    NSArray *stringArray_1 = [string_1 componentsSeparatedByString:@" "]; 
    NSArray *stringArray_2 = [string_2 componentsSeparatedByString:@" "]; 

    // Store only the numbers for comparison in a new array. 
    NSMutableArray *numbers_1 = [[NSMutableArray alloc] init]; 
    NSMutableArray *numbers_2 = [[NSMutableArray alloc] init]; 

    // Make sure the for loop below, runs for the appropriate 
    // number of cycles depending on which array is bigger. 
    int loopMax = 0; 

    if ([stringArray_1 count] > [stringArray_2 count]) { 
     loopMax = (int)[stringArray_1 count]; 
    } 

    else { 
     loopMax = (int)[stringArray_2 count]; 
    } 

    // Now go through the stringArray's and store only the 
    // numbers in the mutable array's. This will be used 
    // during the comparison stage. 
    for (int loop = 0; loop < loopMax; loop++) { 

     NSCharacterSet *notDigits = [[NSCharacterSet decimalDigitCharacterSet] invertedSet]; 

     if (loop < [stringArray_1 count]) { 

      if ([[stringArray_1 objectAtindex:loop] rangeOfCharacterFromSet:notDigits].location == NSNotFound) { 
       // String consists only of the digits 0 through 9. 
       [numbers_1 addObject:[stringArray_1 objectAtindex:loop]]; 
      } 
     } 

     if (loop < [stringArray_2 count]) { 

      if ([[stringArray_2 objectAtindex:loop] rangeOfCharacterFromSet:notDigits].location == NSNotFound) { 
       // String consists only of the digits 0 through 9. 
       [numbers_2 addObject:[stringArray_2 objectAtindex:loop]]; 
      } 
     } 
    } 

    // Now look through the mutable array's 
    // and perform the type comparison,. 

    if ([numbers_1 count] != [numbers_2 count]) { 

     // One of the two strings has more numbers 
     // than the other, so they are NOT compatible. 
     return 1; 
    } 

    else { 

     // Both string have the same number of numbers 
     // numbers so lets go through them to make 
     // sure the numbers are of the same type. 
     for (int loop = 0; loop < [numbers_1 count]; loop++) { 

      // Check to see if the number in the current array index 
      // is a float or an integer. All the numbers in the array have 
      // to be the SAME type, in order for the strings to be compatible. 
      BOOL check_float_1 = [[NSScanner scannerWithString:[numbers_1 objectAtindex:loop]] scanFloat:nil]; 
      BOOL check_int_1 = [[NSScanner scannerWithString:[numbers_1 objectAtindex:loop]] scanInt:nil]; 
      BOOL check_float_2 = [[NSScanner scannerWithString:[numbers_2 objectAtindex:loop]] scanFloat:nil]; 
      BOOL check_int_2 = [[NSScanner scannerWithString:[numbers_2 objectAtindex:loop]] scanInt:nil]; 

      if (check_float_1 == YES) { 

       if (check_float_2 == NO) { 
        return 1; 
       } 
      } 

      else if (check_int_1 == YES) { 

       if (check_int_2 == NO) { 
        return 1; 
       } 
      } 

      else { 
       // Error of some sort...... 
       return 1; 
      } 
     } 

     // All the numbers in the strings are of the same 
     // type (otherwise we would NOT have reached 
     // this point). Therefore the strings are compatible. 
     return 0; 
     } 
} 
+1

Das erste Problem besteht darin, die Strings nach Leerzeichen aufzuteilen. Warum? – Sulthan

+0

@Sultan Ich spalte sie nicht durch Leerzeichen. Was ich dort mache, ist, eine Kopie aller einzelnen Strings in dieser Zeichenfolge zu bekommen und sie in separaten Array-Elementen zu speichern. Nehmen wir an, die Zeichenfolge war "Hallo 123", dann nach "' 'componentsSeparatedByString''', würde es im Array als" Hallo "[0] und" 123 "[1] gespeichert werden. –

+1

Wie wäre es mit "Something% d" und "Something% d"? Ich denke, dass sie basierend auf der Frage als "kompatibel" betrachtet werden. –

9

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.

+0

Ich mag diese Antwort wirklich. Wenn es dir gut geht, hast du diese Prämie verdient. – Mekap

+0

Ich hatte keine Zeit, den Code zu lesen, auszuführen oder zu testen, aber es scheint, als ob Sie die Frage verstanden haben. Mein Hauptanliegen ist Sicherheit. Ich stellte fest, dass wir Formatstrings aus einer Konfigurationsdatei bezogen, die aus dem Internet heruntergeladen werden konnte. Das bedeutet, dass ein Angreifer eine Formatzeichenfolge einfügen kann. Ich habe das mit einer für unseren Fall spezifischen Implementierung behoben, aber ich dachte mir, dass es eine allgemeine Implementierung geben sollte, um die Formatzeichenkompatibilität zu überprüfen. Während Ihre Implementierung gut funktionieren könnte, fühle ich mich nicht wohl dabei, sie zu verwenden, da ich etwas getestetes und erprobtes ausprobieren möchte. –

+0

@Erik B Die Sicherheitsbedenken, IMO, ändern vor allem den Fokus der Frage. Ich habe meiner Antwort etwas hinzugefügt, um dieses Problem anzugehen, aber Sicherheit ist wirklich eine neue Frage. Vielleicht fügen Sie einen neuen Beitrag mit Details zu Ihren Sicherheitsbedenken hinzu - besonders in den Bereichen Stiche und "% n". IAC, ich glaube nicht, dass Sie eine fertige Implementierung finden werden. – chux