Подтвердить что ты не робот

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

Примеры:

"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, которое тоже отлично.

4b9b3361

Ответ 1

Проверка совместимости строк формата 2 printf() - это упражнение в разборе формата.

C, по крайней мере, не имеет стандартной функции сравнения во время выполнения, такой как:

int format_cmp(const char *f1, const char *f2) 

Форматы, такие как "%d %f" и "%i %e", очевидно, совместимы с тем, что оба ожидают a int и float/double. Примечание: float повышается до double, поскольку short и signed char повышаются до int.

Форматы "%*.*f" и "%i %d %e" совместимы, но не очевидны: оба ожидают a 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.

[изменить]

Комментарии OP о проблемах безопасности. Это изменяет характер сообщения и сравнивает его с безопасным. Я бы предположил, что один из шаблонов (A) будет эталонным шаблоном, а следующий (B) будет тестом. Тест будет "В, по крайней мере, такой же безопасный, как A?". Пример A = "%.20s" и B1 = "%.19s", B2 = "%.20s", B3 = "%.21s". B1 и B2 проходят тест безопасности, так как они не извлекают больше 20 char. B3 - проблема, поскольку она проходит контрольный предел 20 char. Кроме того, любая неширина, квалифицированная с помощью %s %[ %c, является проблемой безопасности - в эталонном или тестовом шаблоне. Этот код ответа не решает эту проблему.

Как уже упоминалось, код еще не обрабатывает модификаторы с помощью "%n".

Ответ 2

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

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