visual numero number net moneda formato ejemplo decimales objective-c c printf compatibility format-string

objective-c - numero - string.format visual basic



¿Cómo comprobar que dos cadenas de formato son compatibles? (2)

Ejemplos:

"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

Pensé que debería haber alguna función C para esto, pero no obtengo ningún resultado de búsqueda relevante. Me refiero a que el compilador está comprobando que la cadena de formato y los argumentos coinciden, por lo que el código para verificar esto ya está escrito. La única pregunta es cómo puedo llamarlo.

Estoy usando Objective-C, así que si hay una solución específica para Objective-C también está bien.


Comprobar si 2 cadenas de formato printf() son compatibles es un ejercicio de análisis de formato.

C, al menos, no tiene una función estándar de comparación en tiempo de ejecución, como por ejemplo:

int format_cmp(const char *f1, const char *f2); // Does not exist

Formatos como "%d %f" y "%i %e" son obviamente compatibles ya que ambos esperan un int y un float/double . Nota: float se promueve para double como short y signed char se promueve a int .

Los formatos "%*.*f" y "%i %d %e" son compatibles, pero no obvios: ambos esperan un int , int y float/double .

Los formatos "%hhd" y "%d" esperan un int , aunque el primero tendrá sus valores para el signed char antes de imprimir.

Los formatos "%d" y "%u" no son compatibles. A pesar de que muchos sistemas se comportaron como se esperaba. Nota: Por lo general, char fomentará a int .

Los formatos "%d" y "%ld" no son estrictamente compatibles. En un sistema de 32 bits hay equivalentes, pero no en general. Por supuesto, el código se puede modificar para acomodar esto. OTOH "%lf" y "%f" son compatibles debido a las promociones de argumentos habituales de float a double .

Los formatos "%lu" y "zu" pueden ser compatibles, pero eso depende de la implementación de unsigned long y size_t . Las adiciones al código podrían permitir esta o equivalencias relacionadas.

Algunas combinaciones de modificadores y especificadores no se definen como "zp" . Lo siguiente no deshabilita tales combinaciones esotéricas, pero sí las compara.

Los modificadores como "$" son extensiones del estándar C y no se implementan en lo siguiente.

La prueba de compatibilidad para printf() difiere de 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; }

Nota: solo se realizan pruebas mínimas. Se podrían agregar muchas consideraciones adicionales.

Deficiencias conocidas: modificadores hh,h,l,ll,j,z,t con n . l con s,c .

[Editar]

OP comenta sobre preocupaciones de seguridad. Esto cambia la naturaleza de la publicación y la comparación de una de igualdad a una de seguridad. Me imagino que uno de los patrones (A) sería un patrón de referencia y el siguiente (B) sería la prueba. La prueba sería "¿B es al menos tan segura como A?". Ejemplo A = "%.20s" y B1 = "%.19s" , B2 = "%.20s" , B3 = "%.21s" . B1 y B2 pasan la prueba de seguridad ya que no extraen más el 20 char . B3 es un problema ya que pasa el límite de referencia de 20 caracteres. Además, cualquier ancho no calificado con %s %[ %c es un problema de seguridad: en el patrón de referencia o prueba. El código de esta respuesta no aborda este problema.

Como se mencionó, el código aún no maneja los modificadores con "%n" .

[2018 edit]

En relación con "Formatos "%d" y "%u" no son compatibles.": Esto es para que los valores se impriman en general. Para valores en el rango [0..INT_MAX] , cualquiera de los formatos puede funcionar por C11dr §6.5.2.2 6.


Mi comprensión de lo que quieres es que, básicamente, quieres un método que pueda mirar dos cadenas y detectar si ambos tienen los mismos tipos de valores. O algo así como esas líneas ... Si es así, prueba esto (o algo parecido a esto):

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