1
votes

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!

4
Would be simple enough to read in the whole line and parse it yourself. 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.Crowman

4 Answers

4
votes

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$ 
1
votes

Here's a simple implementation:

#include <stdio.h>
#include <string.h>
#include <ctype.h>

int main(){
    char line[128] = "Boston \"New York\" \"San Francisco\" Memphis\n";
    //fgets(line, sizeof(line), stdin);//Boston "New York" "San Francisco" Memphis
    char array[10][32];
    char *p;
    int i, n, len;

    n = 0;
    for(p = line ; *p ; p += len){
        while(isspace(*p))//skip white space
            ++p;
        if(!*p)break;
        if(*p=='"'){
            sscanf(p, "\"%31[^\"]\"%n", array[n++], &len);
        } else {
            sscanf(p, "%31s%n", array[n++], &len);
        }
    }
    for(i=0;i<n;++i){
        printf("%s\n", array[i]);
    }
    return 0;
}
0
votes

You can probably break this into 2 different problems:

  1. If you see a ", read chars until the next ", then copy the string in between the quotes to the next available index of your multidimensional array

  2. Read char until you see space character, then copy the string(word) between last space and current space to the next available index of your multidimensional array.

This is assuming your input is properly formatted and each quote has a corresponding pair. i.e. it doesn't have "Memphis or something like that.

-2
votes

Why not assign null to byte after each city name?

This will require a simple algorithm passing a char array only once. At the same time, you can add pointers to the beginning of each city name in the array of pointers. So you get multidimensional char array.

The only problem - remove the quotation marks. Small refinement of the original algorithm will solve this problem.