2

I'm making a program that takes names from the user seperated by commas. The program allows the user to put as many or as little spaces as they want between the commas. So for example:

If I were to type something like

Smith, John

or

Smith,John

I would want to print out

John, Smith

The thing is though my program does not properly process the following examples above; it works if the input was something like

Smith , John

or

Smith ,John.

Here is my code:

#include <stdio.h>
#include <string.h>
#include <ctype.h>
#define LINESIZE 128

int get_last_first(FILE *fp);

int main (void)
{
    get_last_first(stdin);
}

/*takes names in the format [LASTNAME],[FIRSTNAME]*/
int get_last_first(FILE *fp)
{
    char first[LINESIZE];
    char last[LINESIZE];
    char line[LINESIZE];
    size_t i;

    while(1)
    {
        printf("Enter your last name followed by a comma and your first name\n");

        /*if we cant read a line from stdin*/
        if(!fgets(line, LINESIZE, fp)) 
        {
            clearerr(stdin);
            break;   /*stop the loop*/
        }

        /*goes through the line array and checks for non-alphabetic characters*/        
        for(i = 0; i < strlen(line); i++)
        {
            if(!isalpha(line[i]))
            {
                /*if it sees a space hyphen or comma, it continues the program*/
                if((isspace(line[i]) || line[i] == ',') || line[i] == '-' )
                {
                    continue;
                }
                else
                {
                    return -1;
                }
            }

        }

        if(sscanf(line, "%s , %s", last, first))
        {
            printf("%s, %s", first, last);
            return 1;
        }

        return 0;   

    }
}

Is it because I am not using sscanf properly?

Thomas Dickey
  • 51,086
  • 7
  • 70
  • 105
user798774
  • 403
  • 2
  • 8
  • 20

2 Answers2

2

sscanf() doesn't do pattern matching; a comma is a perfectly valid non-whitespace string component, so will be swallowed by a %s specification. You can use something like %[^ \t,] to match a run of characters up to whitespace or a comma.

geekosaur
  • 59,309
  • 11
  • 123
  • 114
  • If you don't mind can you tell me how to properly use it for this case, I haven't used Regex in a while, so i'm a bit rusty – user798774 Jun 23 '11 at 18:16
  • It's not a regex, is the problem. :) `%s` reads a sequence of non-whitespace; `%[xyz]` reads a sequence of the characters `x`, `y`, or `z`, while `%[^xyz]` reads a sequence of characters *other than* `x`, `y`, or `z`. Otherwise, it's literal: there are no magic characters other than `%`, and any specifier other than `%[...]` or `%c` stops at whitespace. – geekosaur Jun 23 '11 at 18:40
1

The basic problem is outlined in geekosaur's answer - yes, you are misusing sscanf().

There are other problems in your code too.

  • Do not use strlen() in the test of a loop; it gets called each time and produces the same answer (unless you're hacking the string as you go). Call it once before the loop and use the saved value.
  • Stylistically, you have unnecessary parentheses and spaces (and miss necessary spaces) in the condition:

    if((isspace(line[i]) || line[i] == ',') || line[i] == '-' )
    
    if (isspace(line[i]) || line[i] == ',' || line[i] == '-')
    

    There should be a space after the keyword (see the standard). There's no need to break the symmetry of the three-way condition with the extra parentheses; and there's no need for the space before the final close parenthesis. (Or, if you insist on it, then you need a balancing open space after the opening parenthesis of the condition. But please don't insist on it.)

  • You need test that sscanf() successfully converted two values; you just check that it did not convert 0 values. It might return 1; it might return EOF (as well as 0 or 2).

  • Your 'infinite loop' is broken by a break, but then the function does not return a value.
  • Your main() program ignores the returned value from the function - so you didn't need to the return value after all.
  • Stylistically, although the C99 standard permits you to leave out the return at the end of main(), I for one prefer to see it there. (You did not commit the sin of having main() return void; thank you.)
  • You might have noticed that I removed some stray code embedded in comments when I formatted the question. Don't keep dead code around - and don't show it off on SO.
  • Stylistically, I put the space between #include and <stdio.h>. Again, take a look at the C standard and notice how they write it: #include <stdio.h> and do thou likewise.

The comments tagged 'stylistically' are recommendations - the compiler is largely oblivious to spaces or lack of spaces. Nevertheless, the C standard shows all examples with spaces in #include lines and spaces after the keywords such as for and if, and the original description of C by Kernighan and Ritchie does the same, so there is solid precedent for you to follow. Generally, other people will find your code easier to read if you follow the standard conventions. The positioning of braces is more controversial - but you won't go far wrong if you follow K&R there (even though I prefer the Allman style).

Community
  • 1
  • 1
Jonathan Leffler
  • 730,956
  • 141
  • 904
  • 1,278
  • Strictly, there is no need to use a space after the keyword, or between the #include and the . I mean, I know you meant well, teach good practices of code and stuff... I think it's valid, but I think you should make it clear that the standard does not enforce it, even though people should do it. – jpmelos Jun 23 '11 at 05:32