Search code examples
cscanffgets

Reading strings from stdin


I'm trying to read a line from stdin with this kind of format:

Boston "New York" "San Francisco" Memphis (note that string with a space in them are between brackets. Also note that each city name is separated by a space.) I've tried to read this with scanf one at a time, fgets the whole line and then tokenizing, but with bad results. What I pretend is to store everything in a muldimensional char array for later use.

Any suggestions on how I should solve this problem? Thank you in advance!


Solution

  • You can read in the whole line and parse it yourself easily enough. If the first non-whitespace character you come to is not a ", then read until the next space. If it is, then read until the next ", presuming you don't need to worry about escaped quotes.

    Here's a simple implementation:

    #include <stdio.h>
    #include <string.h>
    #include <stdlib.h>
    #include <ctype.h>
    
    #define MAX_BUFFER 100
    #define MAX_STRINGS 10
    
    int main(void) {
        char buffer[MAX_BUFFER];
    
        if ( !fgets(buffer, MAX_BUFFER, stdin) ) {
            fprintf(stderr, "Couldn't get input.\n");
            return EXIT_FAILURE;
        }
        else {
    
            /*  Remove trailing newline, if present  */
    
            size_t length = strlen(buffer);
            if ( length && buffer[length - 1] == '\n' ) {
                buffer[length - 1] = '\0';
            }
        }
    
        char *my_strings[MAX_STRINGS + 1] = {NULL};
        int read_strings = 0;
        char *buf_ptr = buffer;
    
        while ( *buf_ptr && read_strings < MAX_STRINGS ) {
            char temp_buf[MAX_BUFFER] = {0};
            char *temp_ptr = temp_buf;
    
            /*  Skip leading whitespace  */
    
            while ( *buf_ptr && isspace(*buf_ptr) ) {
                ++buf_ptr;
            }
    
            if ( *buf_ptr ) {
                if ( *buf_ptr == '"' ) {
    
                    /*  If starts with '"', read to next '"'...  */
    
                    ++buf_ptr;      /*  Skip first "  */
                    while ( *buf_ptr && *buf_ptr != '"' ) {
                        *temp_ptr++ = *buf_ptr++;
                    }
    
                    if ( *buf_ptr ) {
                        ++buf_ptr;  /*  Skip second "  */
                    }
                }
                else {
    
                    /*  ...otherwise, read to next whitespace  */
    
                    while ( *buf_ptr && !isspace(*buf_ptr) ) {
                        *temp_ptr++ = *buf_ptr++;
                    }
                }
    
                /*  Copy substring into string array  */
    
                my_strings[read_strings] = malloc(strlen(temp_buf) + 1);
                if ( !my_strings[read_strings] ) {
                    fprintf(stderr, "Couldn't allocate memory.\n");
                    return EXIT_FAILURE;
                }
                strcpy(my_strings[read_strings++], temp_buf);
            }
        }
    
        for ( size_t i = 0; my_strings[i]; ++i ) {
            printf("String %zu: %s\n", i + 1, my_strings[i]);
            free(my_strings[i]);
        }
    
        return 0;
    }
    

    Sample output:

    paul@MacBook:~/Documents/src/scratch$ ./ql
    Boston "New York" "San Francisco" Memphis
    String 1: Boston
    String 2: New York
    String 3: San Francisco
    String 4: Memphis
    paul@MacBook:~/Documents/src/scratch$ ./ql
    a quoted "word" and "some quoted words" and an "unclosed quoted string
    String 1: a
    String 2: quoted
    String 3: word
    String 4: and
    String 5: some quoted words
    String 6: and
    String 7: an
    String 8: unclosed quoted string
    paul@MacBook:~/Documents/src/scratch$