2015-03-09 5 views
39

Примеры:Как проверить совместимость двух строк формата?

"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 

Я полагал, что должен быть какой-то C функция для этого, но я не получаю никаких релевантных результатов поиска. Я имею в виду, что компилятор проверяет соответствие строки формата и аргументов, поэтому код для проверки этого уже написан. Вопрос только в том, как я могу это назвать.

Я использую Objective-C, поэтому, если есть конкретное решение Objective-C, это тоже хорошо.

+1

Возможно, вы сможете сгибать 'NS_FORMAT_FUNCTION' по своему желанию. [Проверьте этот ответ SO] (http://stackoverflow.com/a/16983220/1292061), а также [Документы Clang для '__format__'] (http://clang.llvm.org/docs/AttributeReference.html # формат-гну-формат). – ravron

+0

Или посмотрите здесь GNU: http://www.gnu.org/software/libc/manual/html_node/Parsing-a-Template-String.html –

+0

'parse_printf_format' выглядит здорово. Как импортировать его? –

ответ

-1

Мое понимание того, что вы хотите, заключается в том, что вы в основном хотите метод, который может смотреть на две строки и определять, имеют ли они оба одинаковые типы значений в них. Или что-то давно эти строки .... Если это так, то попробуйте это (или что-то вдоль линий этого):

-(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

Первой проблемой является разбиение строк на пробелы. Зачем? – Sulthan

+0

@Sulthan Я не разделяю их пробелами. То, что я делаю там, получает копию всех отдельных строк в этой строке и сохраняет их в отдельных элементах массива. Поэтому давайте скажем, что строка была «привет 123», а затем «' componentsSeparatedByString''', она будет храниться в массиве как «привет» [0] и «123» [1]. –

+1

Как насчет «Что-то% d» и «Что-то% d»? Я думаю, они считаются «совместимыми» на основе вопроса. –

9

Проверяется 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.

+0

Мне очень нравится этот ответ. Если у вас все в порядке, вы заслужили эту щедрость. – Mekap

+0

У меня не было времени на чтение, запуск или тестирование кода, но похоже, что вы поняли вопрос. Моя главная забота - безопасность. Я обнаружил, что мы получаем строки формата из файла конфигурации, который можно загрузить из Интернета. Это означает, что злоумышленник может ввести строку формата. Я исправил это с реализацией, специфичной для нашего случая, но я решил, что должна быть общая реализация для проверки совместимости строк формата. В то время как ваша реализация может работать нормально, я не чувствую себя комфортно, используя ее, поскольку я хочу, чтобы что-то хорошо протестировано и доказано сражение. –

+0

@Erik B Проблемы безопасности, ИМО, важно изменить фокус вопроса. Я добавил некоторые ответы на этот вопрос, но безопасность - это действительно новый вопрос. Возможно, добавьте новое сообщение с подробными сведениями о ваших проблемах с безопасностью - особенно в области укусов и «% n». IAC, я не думаю, что вы найдете готовое воплощение. – chux

 Смежные вопросы

  • Нет связанных вопросов^_^