如何检查两个格式string是否兼容?

例子:

"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函数,但我没有得到任何相关的search结果。 我的意思是编译器正在检查格式string和参数是否匹配,所以用于检查的代码已经写好了。 唯一的问题是我怎样称呼它。

我正在使用Objective-C,所以如果有一个Objective-C特定的解决scheme也可以。

检查2个printf()格式的string是否兼容是格式parsing的练习。

C至less没有标准的运行时比较function,例如:

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

"%d %f""%i %e"这样的格式显然是兼容的,因为它们都需要intfloat/double 。 注意: float被提升为double因为shortsigned char被提升为int

格式"%*.*f""%i %d %e"是兼容的,但不是显而易见的:都需要intintfloat/double

格式"%hhd""%d"都需要一个int ,即使第一个值会在打印之前将值转换为带signed char值。

格式"%d""%u" 兼容。 尽pipe许多系统会像希望的那样行事。 注意:通常char会提升为int

格式"%d""%ld" 严格兼容。 在32位系统上有相当的,但不是一般的。 当然代码可以改变以适应这个。 OTOH "%lf""%f" 兼容的,因为floatdouble参数促销活动。

格式"%lu""zu" 可能是兼容的,但这取决于unsigned longsize_t 。 代码的添加可以允许这个或相关的等价。

修饰符和说明符的某些组合没有像"zp"那样定义。 以下并不禁止这种深奥的组合 – 但不会比较它们。

"$"这样的修饰符是标准C的扩展,并不在下面实现。

printf()的兼容性testing不同于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; } 

注意:只做最less的testing。 许多额外的考虑可以被添加。

已知的缺点: hh,h,l,ll,j,z,t修饰符与nls,c

[编辑]

OP关于安全问题的评论。 这改变了这个职位的性质以及从一个平等到一个安全的比较。 我会想象其中一个模式(A)将是一个参考模式,下一个(B)将是testing。 testing将是“B至less和A一样安全?”。 示例A = "%.20s"B1 = "%.19s"B2 = "%.20s"B3 = "%.21s"B1B2都通过安全testing,因为它们不会提取更多的20个charB3是一个问题,因为它通过20个char的参考极限。 此外, 任何非宽度限定%s %[ %c是一个安全问题 – 在参考或testing模式。 这个答案的代码没有解决这个问题。

如上所述,代码还没有处理带有"%n"修饰符。

我对你想要的东西的理解是,你基本上需要一个方法,它可以看两个string,并检测它们是否都有相同types的值。 或者是那些长的行….如果是这样,那么试试这个(或者沿着这个行):

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