Specifying the maximum string length to scanf dynamically in C (like “%*s” in printf)

后端 未结 3 339
小蘑菇
小蘑菇 2020-12-06 04:51

I can specify the maximum amount of characters for scanf to read to a buffer using this technique:

char buffer[64];

/* Read one li         


        
相关标签:
3条回答
  • 2020-12-06 05:20

    There is indeed no variable width specifier in the scanf family of functions. Alternatives include creating the format string dynamically (though this seems a bit silly if the width is a compile-time constant) or simply accepting the magic number. One possibility is to use preprocessor macros for specifying both the buffer and format string width:

    #define STR_VALUE(x) STR(x)
    #define STR(x) #x
    
    #define MAX_LEN 63
    
    char buffer[MAX_LEN + 1];
    fscanf(file, "%" STR_VALUE(MAX_LEN) "[^\n]", buffer);
    
    0 讨论(0)
  • 2020-12-06 05:20

    Another option is to #define the length of the string:

    #define STRING_MAX_LENGTH "%10s"
    

    or

    #define DOUBLE_LENGTH "%5f"
    
    0 讨论(0)
  • 2020-12-06 05:35

    Basic answer

    There isn't an analog to the printf() format specifier * in scanf().

    In The Practice of Programming, Kernighan and Pike recommend using snprintf() to create the format string:

    size_t sz = 64;
    char format[32];
    snprintf(format, sizeof(format), "%%%zus", sz);
    if (scanf(format, buffer) != 1) { …oops… }
    

    Extra information

    Upgrading the example to a complete function:

    int read_name(FILE *fp, char *buffer, size_t bufsiz)
    {
        char format[16];
        snprintf(format, sizeof(format), "%%%zus", bufsiz - 1);
        return fscanf(fp, format, buffer);
    }
    

    This emphasizes that the size in the format specification is one less than the size of the buffer (it is the number of non-null characters that can be stored without counting the terminating null). Note that this is in contrast to fgets() where the size (an int, incidentally; not a size_t) is the size of the buffer, not one less. There are multiple ways of improving the function, but it shows the point. (You can replace the s in the format with [^\n] if that's what you want.)

    Also, as Tim Čas noted in the comments, if you want (the rest of) a line of input, you're usually better off using fgets() to read the line, but remember that it includes the newline in its output (whereas %63[^\n] leaves the newline to be read by the next I/O operation). For more general scanning (for example, 2 or 3 strings), this technique may be better — especially if used with fgets() or getline() and then sscanf() to parse the input.

    Also, the TR 24731-1 'safe' functions, implemented by Microsoft (more or less) and standardized in Annex K of ISO/IEC 9899-2011 (the C11 standard), require a length explicitly:

    if (scanf_s("%[^\n]", buffer, sizeof(buffer)) != 1)
        ...oops...
    

    This avoids buffer overflows, but probably generates an error if the input is too long. The size could/should be specified in the format string as before:

    if (scanf_s("%63[^\n]", buffer, sizeof(buffer)) != 1)
        ...oops...
    
    if (scanf_s(format, buffer, sizeof(buffer)) != 1)
        ...oops...
    

    Note that the warning (from some compilers under some sets of flags) about 'non-constant format string' has to be ignored or suppressed for code using the generated format string.

    0 讨论(0)
提交回复
热议问题