-1

I'm in my first programming class and having trouble with a project of ours. The program is designed to take a string's inputs and see if they match the pattern and recognize if the pattern is broken; in this case it is meant to recognize if the user inputs "hahaha!", "hohohoho!", or a mixture of the two 'ha' and 'ho' (always ending in '!').

My trouble is that I have started an attempt at this code using switch cases, but do not know if this is the most effective way to program for the project, or if it is even possible to do it this way.

Here is my code so far, please help me.

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

#define string_len 100

int main ()
{     
    char string[string_len];
    int state = 0;

    while(1)
    {
        for(int i = 0; i < string_len; i++)
        {
            printf("Hello, I can tell if you are laughing or not, you can exit by typing 'bye': \n");
            scanf("%s", string);

            for(state = 0; state < 5;)
            {
                switch(state)
                {        
                    case 0: 
                        if(strcmp(string, "bye") == 0)
                            printf("Bye now!\n");
                        return 0;
                        break;

                    case 1: 
                        if(string[i] == 'h')
                            state++;
                        else(printf("you are not laughing\n"));
                            break;

                    case 2: 
                        if(string[i] == 'a')
                            state--;
                        else(state++);
                            break;

                    case 3: 
                        if(string[i] == 'o')
                            state = state-2;
                        else(printf("you are not laughing\n"));
                            break;

                    case 4: 
                        if(string[i] == '!')
                            printf("You are laughing!\n");
                        else(printf("You are not laughing\n"));

                }
            }
        }

        return 0;
    }
}

I think that I may be mixed up with the state part of my program in the switch. I'm trying to allow it to go from:

state 0 : check if it says bye, if so "bye now"

state 1: is it an h? if so, check for a or o, if not "you arent laughing"

state 2: is it an a? if so, check for an 'h' or '!' -This is where I'm especially confused, if not is it an o?

state 3: is it an o? if so, check for an 'h' or '!', if not "you aren't laughing"

state 4: is it an '!'? if so "you are laughing" if not, "you are not laughing"

I hope I have formatted this question well enough, please let me know if I could make this more readable in any way and if you have any questions for me.

Thank you all for the help.

JohnX
  • 29
  • 3

3 Answers3

0

Since you asked if there might be another way preferable to a switch() statement, I decide to code an example of a way for you. This works for me. Should compile cleanly and run fine.

#include <stdio.h>
#include <string.h>
#define MAXLEN 100
char *readline();

int main(void) {
    char string[MAXLEN];
    for(;;) {
        memset(string, '\0', MAXLEN);
        printf("Hello, I can tell if you are laughing or not, you can exit by typing 'bye': \n");
        readline(string, MAXLEN, stdin);
        int i = 0;
        int aborted = 0;
        char buf[3] = { 0 };
        while (i < strlen(string) - 1) {
           buf[i % 2] = string[i];
           if (i % 2 == 1) {
              if (strncmp(buf, "ha", 2) != 0 && strncmp(buf, "ho", 2) != 0) {
                  printf("\nYou are NOT laughing [1]\n\n");
                  aborted = 1;
                  break;
              }
           }
           i++;
        }
        if (!aborted) {
            if (string[i] != '!') {
                printf("\nYou are NOT laughing [2]\n\n");
                continue;
            }
            printf("\nYou ARE laughing!\n\n");
        }
    }
}

char *readline (char *buf, size_t length, FILE *f) {
  char *p;
  if ((p = fgets (buf, length, f)) != NULL) {
    size_t last = strlen (buf) - 1;
    if (buf[last] == '\n') {
      buf[last] = '\0';
    } else {
      fscanf (f, "%*[^\n]");
      (void) fgetc (f);
    }
  }
  return p;
} 
clearlight
  • 12,255
  • 11
  • 57
  • 75
0

The problem you are having is that you fail to remove the newline from stdin. Instead of:

scanf("%s", string);

You need:

scanf("%[^\n]%*c", string);

What happens is that you read your first input fine, but stdin still contains a newline '\n' (the result of pressing [Enter]). If you don't enter bye, when you reach scanf again, scanf takes the '\n' as your input. The new format string above "%[^\n]%*c" says %[^\n] read all characters up to the newline, then %*c read and discard the newline.

David C. Rankin
  • 81,885
  • 6
  • 58
  • 85
  • What prevents that from overrunning string? – clearlight Apr 06 '15 at 01:11
  • Well that part is cool, in fact I didn't know about those special format codes, so I will study it. But it is prone to a buffer overflow attack and would not be allowed in any code that was considered secure, right? – clearlight Apr 06 '15 at 01:13
  • No, it's fine, if you want, you can specifically limit it to a number of characters with a `width` specification. E.g. `scanf("%10[^\n]%*c", string);` to limit input to `10 chars`. – David C. Rankin Apr 06 '15 at 01:15
  • `scanf` doesn't do anything if you enter more than 10 chars, they are left in the input buffer. To empty the input buffer completely, simply add `int c; while ((c = getchar()) != '\n' && c != EOF);` (in fact you can leave your format statement, and simply add that line below your original `scanf` statement. (it will be less secure that limiting to 10 chars (or whatever). You see `"%[^\n]"` and `"%s"` are functionally equivalent from a security standpoint. – David C. Rankin Apr 06 '15 at 01:19
  • It is well worth spending 15-20 min in `man scanf`. It can do a whole lot more that I ever realized either. It does have its limitations, but knowing how to use all the tools it provides makes is quite flexible (it will even allocate memory for you, see `%m` [`%a` on older versions of windows]). – David C. Rankin Apr 06 '15 at 01:23
  • @DavidCRankin I did replace `readline()` in my code with `scanf()`. It worked, but the program looped infinitely when I entered an empty line. And the `getchar()` loop caused it to hang. So some refinement needed. But I'm going to scour the `scanf()` man page closely in any case for future reference. – clearlight Apr 06 '15 at 01:27
  • Oh yes, it's not real smart, you need to **check the return** of `scanf`. Each time it matches a pattern (like `%s`) it adds 1 to the return. When you `suppress assignment` with `*`, it simply reads and discards without adding it to the return. So only `if (scanf("%10[^\n]%*c", string) == 1) ` – David C. Rankin Apr 06 '15 at 01:33
0
#include <stdio.h>

#define string_len 100
#define n2s_(n) #n
#define n2s(n) n2s_(n)

int main(void){  
    char string[string_len+1];

    while(1){
        printf("Hello, I can tell if you are laughing or not, you can exit by typing 'bye': \n");
        scanf("%" n2s(string_len) "s", string);
        if(strcmp(string, "bye") == 0)
            break;
        //match (ha | ho)+! ?
        int i, match = 1, ch;
        for(i = 0; match && (ch=string[i]) && ch != '!'; ++i){
            if(i & 1){//odd position
                if(string[i] != 'a' && string[i] != 'o'){
                    match = 0;
                }
            } else {//even position
                if(string[i] != 'h'){
                    match = 0;
                }
            }
        }
        if(match && i != 0 && ch == '!')
            printf("You are laughing!\n");
        else
            printf("You are not laughing\n");
    }

    return 0;
}
BLUEPIXY
  • 39,699
  • 7
  • 33
  • 70