Search code examples
cstringstdio

How to check string matches format "printf like - %d/..."


I have dynamic string like: "/users/5/10/fnvfnvdjvndfvjvdklchsh" and also dynamic format like "/users/%u/%d/%s", how to check these strings matches?

As string i mean char[255] or char* str = malloc(x).

I tried use sscanf but i dont know number of arguments and types, if i do:

int res = sscanf(input, format);

I have stack overflow, or can i allocate stack to prevent this? Example like this:

void* buffer = malloc(1024);
int res = sscanf(input, format, buffer);

I would like have function like this:

bool stringMatches(const char* format, const char* input);

stringMatches("/users/%u/%d/%s", "/users/5/10/fnvfnvdjvndfvjvdklchsh"); //true
stringMatches("/users/%u/%d/%s", "/users/5/10"); //false
stringMatches("/users/%u/%d/%s", "/users/-10/10/aaa"); //false %u is unsigned

Do you see any solution?
Thanks in advance.


Solution

  • I don't think that there is a scanf-like matching function in the standard lib, so you will have to write your own. Replicating all details of the scanf behaviour is difficult, but it's probably not necessary.

    If you allow only % and a limited selection of single format identifiers without size, width and precision information, the code isn't terribly complex:

    bool stringMatches(const char *format, const char *input)
    {
        while (*format) {
            if (*format == '%') {
                format++;
    
                switch(*format++) {
                case '%': {
                        if (*input++ != '%') return false;
                    }
                    break;
    
                case 'u': 
                        if (*input == '-') return false;
                        // continue with 'd' case
    
                case 'd': {                
                        char *end;
    
                        strtol(input, &end, 0);
                        if (end == input) return false;
                        input = end;
                    }
                    break;
    
                case 's':  {
                        if (isspace((uint8_t) *input)) return false;
    
                        while (*input && !isspace((uint8_t) *input)) input++;
                    }
                    break;
    
                default: 
                        return false;
                }
            } else {
                if (*format++ != *input++) return false;
            }
        }
    
        return (*input == '\0');
    }
    

    Some notes:

    • I've parsed the numbers with strtol. If you want to include floating-point number formats, you could use strtod for that, if your embedded system provides it. (You could also parse stretches of isdigit() chars as valid numbers.)
    • The 'u' case falls through to the 'd' case here. The function strtoul parses an unsigned long, but it allows a minus sign, so that case is caught explicitly. (But the way it is caught, it won't allow leading white space.)
    • You could implement your own formats or re-interpret existing ones. For example you could decide that you don't want leading white space for numbers or that a string ends with a slash.