Home > Mobile >  How do I read a whole line to a string?
How do I read a whole line to a string?

Time:06-26

int run_add_line_after(Document *doc, char *command) {
    int paragraph_num, line_num;
    char com[MAX_STR_SIZE   1], extra[MAX_STR_SIZE   2], line[MAX_STR_SIZE   1];

    if (sscanf(command, " %s %d %d %s", com, &paragraph_num, &line_num, extra)
        == 4 && paragraph_num > 0 && line_num >= 0 && extra[0] == '*') {
        strcpy(line, &(extra[1]));
        if (add_line_after(doc, paragraph_num, line_num, line) == FAILURE) {
            printf("add_line_after failed\n");
        }
        return SUCCESS;
    }
    return FAILURE;
}

I want sscanf to read everything left in command to extra but it's only taking the first word. For example, if I have:

  • command: "add_line_after 1 0 *first line of the document"

I want to see:

  • com: "add_line_after"
  • paragraph_num: 1
  • line_num: 0
  • extra: "first line of the document"

but instead I get:

  • com: "add_line_after"
  • paragraph_num: 1
  • line_num: 0
  • extra: "first"

because %s stops when it hits the space. How do I read the rest of the line while still ignoring any whitespace between '0' and '*'?

For reference, MAX_STR_SIZE is 80 and command is a 1025 character array (though I don't think that matters). Just assume extra is large enough to hold the rest of the line.

CodePudding user response:

sscanf is really the wrong tool to use here. It can be done, but probably should not be used the way you are trying. Fortunately, you are not passing com to add_line_after, which means it is not necessary to ensure that com is a properly null-terminated string, and this allows you to avoid all of that unnecessary string copying. (If you were passing com, you would either have to copy it, or write a null terminator into command.) You don't want or need to use sscanf to move data at all. You can just use it to parse the numeric values. It's not clear to me if you want to discard any whitespace that follows the *, and this code does not. If you want to do so, removing that whitespace is trivial and left as an exercise for the reader:

int
run_add_line_after(struct document *doc, const char *command)
{
    int paragraph_num, line_num, n;
    const char *line;

    if( 2 == sscanf(command, "%*s %d %d %n", &paragraph_num, &line_num, &n)
        && paragraph_num > 0 && line_num >= 0 && command[n] == '*' )
    {
        line = command   n   1;
        if( add_line_after(doc, paragraph_num, line_num, line) == FAILURE) {
            fprintf(stderr, "add_line_after failed\n");
        } else {
            return SUCCESS;
        }
    }
    return FAILURE;
}

The idea here is to simply use sscanf to figure out where the extra data is in the string and to parse the integer values. This is absolutely the wrong tool to use (the scanf family is (almost) always the wrong tool), and is used here only for demonstration.

-- edit --

But, of course, this doesn't do exactly what you want. It would be much cleaner to move some functionality into add_line_after to handle the newline, but since you also need to remove the newline, it becomes necessary to do something like:

int
run_add_line_after(struct document *doc, char *command)
{
    int paragraph_num, line_num, n;
    char *line;

    if( 2 == sscanf(command, "%*s %d %d %n", &paragraph_num, &line_num, &n)
        && paragraph_num > 0 && line_num >= 0 && command[n] == '*' )
    {
        line = command   n   1;
        line[strcspn(line, "\n")] = '\0';
        if( add_line_after(doc, paragraph_num, line_num, line) == FAILURE) {
            fprintf(stderr, "add_line_after failed\n");
        } else {
            return SUCCESS;
        }
    }
    return FAILURE;
}

This is not ideal. It would be better if you modify the API so that you can avoid both copying the data and modifying the string that you are given.

CodePudding user response:

Got it. Format string should be " %s %d %d %[^\n]s". It will keep reading into the last string variable until it hits the enter key.

  • Related