How to detect if user inserts data with commas (in a desired format) or not?

前端 未结 3 1687
野的像风
野的像风 2021-01-17 05:03

User inserts data in a format:\" [NAME], [SURNAME], [INDEX] \". Errors codes:
0 -- everything is loaded to the structure properly
1 -- not loaded to structure pr

相关标签:
3条回答
  • 2021-01-17 05:21

    Consider using return value from sscanf:

    #include <stdio.h>
    #include <assert.h>
    typedef struct student_t
    {
       char name[20];
       char surname[40];
       int index;
    } student_t;
    
    enum parse_error_t {
       E_OK,
       E_NOT_LOADED_PROPERLY,
       E_ONLY_NAME_LOADED,
       E_NAME_SURNAME_LOADED
    };
    
    enum parse_error_t parse_input(char *buf, student_t *out) {
        int matches_count;
        if (buf == NULL) {
            return E_NOT_LOADED_PROPERLY;
        }
        matches_count = sscanf(buf, "%20s %[^, 40]%*[, ]%d", out->name, out->surname, &out->index);
        switch(matches_count) {
            case 0:
                return E_NOT_LOADED_PROPERLY;
            case 1:
                return E_ONLY_NAME_LOADED;
            case 2:
                return E_NAME_SURNAME_LOADED;
            case 3:
                return E_OK;
            default:
                return E_NOT_LOADED_PROPERLY;
        }
    }
    
    int main() {
        char *in1 = NULL;
        char *in2 = "John";
        char *in3 = "John, Deep";
        char *in4 = "John, Deep, 999";
        student_t student;
    
        assert(parse_input(in1, &student) == E_NOT_LOADED_PROPERLY);
        assert(parse_input(in2, &student) == E_ONLY_NAME_LOADED);
        assert(parse_input(in3, &student) == E_NAME_SURNAME_LOADED);
        assert(parse_input(in4, &student) == E_OK);
    }
    

    String matching expression is based on this answer.

    0 讨论(0)
  • 2021-01-17 05:25

    I would use this pseudo-code:

    isolate first comma-separated token
    if no token, return 1
    if length >= sizeof(s.name), return 1
    copy first token to s.name
    isolate second token
    if no token, return 2
    if length >= sizeof(s.surname), return 2
    copy first token to s.surname
    isolate third token
    if no token, return 3
    if token not numeric, return 3
    set s.index = atoi( third token )
    return 0
    

    If you code that up in C, you should end up with something nice and short and clean and reliable, without too many annoyingly redundant checking and backtracking.

    (Actually, if it was me, I'd use one general-purpose function to do the token isolating all at once, up front, then simply test if the number of found tokens was 0, 1, 2, 3, or more than 3. See this web page for additional ideas.)

    0 讨论(0)
  • 2021-01-17 05:30

    Here what you need if beyond what the scanf familly functions can do, because you want to strip blank characters at the beginning or the end of a name but still want to allow spaces inside a name. IMHO, you should use a dedicated function for that parsing. Code could be:

    /* Find a string delimited with a character from delims.
     * Blanks at the beginning or the end of the string will be trimed out
     * At return, *end points one past the end of the string and
     * *next points after the delimiter (so delimiter will be next[-1])
     * Returns a pointer to the beginning of the string, or NULL if
     * no delimiter was found
     * */
    const char* find(const char * start, char **end, char **next, const char *delims) {
        static const char blanks[] = " \t\r";
        start += strspn(start, blanks);     // trim blanks at the beginning
        *end = strpbrk(start, delims);      // search first delimiter
        if (end == NULL) {
            return NULL;
        }
        *next = *end + 1;                   // next find will start after the delimiter
        while(*end > start) {               // trim blanks at the end
            bool found = false;
            for (int i=0; i<sizeof(blanks); i++) {
                if ((*end)[-1] == blanks[i]) {
                    --*end ;
                    found = true;
                    break;
                }
            }
            if (! found) break;
        }
        return start;
    }
    // parse a line to fill a student_t
    struct student_t* getstruct(struct student_t *p, int *err_code) {
        char buffer[1024];
        *err_code = 1;       // be prepared to worst case
        *buffer = '\0';
        if (fgets(buffer,1024, stdin)!=NULL) 
        {
            char *end, *next;
            const char delims[] = ",\r\n";
            const char *name = find(buffer, &end, &next, delims) ; // returns pointer to the beginning of the token
            if (name && (next[-1] == ',')) {  // control the delimiter
                int l = end - name;
                if (l > 19) l = 19;
                memcpy(p->name, name, l);
                p->name[l] = '\0';
                *err_code = 2;                // Ok, we have a name followed with a comma
                const char *surname = find(next, &end, &next, delims);
                if (surname && (next[-1] == ',')) { // control delimiter
                    int l = end - surname;
                    if (l > 19) l = 19;
                    memcpy(p->surname, surname, l);
                    p->surname[l] = '\0';
                    *err_code = 3;            // Ok, we have a name followed with a comma
                    if (*end != ',') return NULL;
                    const char *index = find(next, &end, &next, delims);
                    if (index) {              // no need to control the delimiter: scanf will control  
                        char dummy[2];        // that there is no non blank char after the index
                        if (1 == sscanf(index, "%d%1s", &(p->index), dummy)) {
                            *err_code = 0;
                        }
                    }
                }
            }
        }
        return (*err_code == 0) ? p : NULL;
    }
    
    0 讨论(0)
提交回复
热议问题