2

So, I've looked at several posts asking the same question, and none of them have worked for me. Well, they halfway work, as in they work when I'm not doing what I'm doing here. What I'm trying to do is have it display a few lines of text, ask for enter to continue, and display more text so that it doesn't just give you a huge text wall to read through all at once.

Here's everything I've got, and I'll mark where I've tried the wait for enter thing. I'm on windows if it makes a difference.

#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<process.h>
#include<conio.h>
#include<ctype.h>


//void playerName (char* playername){ 
    //FILE* playerNameFile = fopen("player.data","w");
    //fprintf(playerNameFile, "%s", playerName);
//}


int main(){
    char response;
    char playername[20];
    char enter = 0; //For Enter
    system("cls");
    printf("Would you like to embark on an adventure?\n Y/N?:\n\n\n\n");
    response = toupper(getch());
     if (response=='Y'){
        printf("Before we start, what's your name?\n Your name is: ");
        scanf("%s",playername);
        printf("%s, Story Text.\n");
        printf("Story Text\n");
        printf("Story Text.\n");
        printf("Story Text\n");
        printf("Story Text\n");
        printf("Press enter to continue\n");
        while (enter != '\r' && enter != '\n') { enter = getchar(); } //For Enter
        printf("Story Text\n");
        printf("Story Text\n");
        printf("Story Text");
        printf("Story Text \n");
        printf("Story Text\n");
        printf("Story Text\n");
        printf("Story Text\n");
        printf("Press enter to continue\n");
        while (enter != '\r' && enter != '\n') { enter = getchar(); } //For Enter
        printf("Story Text\n");
        printf("Story Text\n\n");
        printf("Story Text\n");
        printf("Story Text\n");
     }
     else if (response=='N'){
        printf("Well, I'll see you later then!");
        exit(0);
     }

        printf("Embark on your quest?\n Y/N?");
        response = toupper(getch());
        if (response=='Y'){
            system("cls");
            printf("'\nStory Text\n");
            printf("'Story Text\n");
            printf("The end for now");  
        }
        else if (response=='N'){
            printf("Come back when you are ready to embark on your quest.");
            exit(0);
    }


    return 0;
}

I'm assuming the while part is the problem with it, but I can't think of a way to make it work within the conditional I've got. Is there a way this can be done?

hego64
  • 345
  • 1
  • 5
  • 17
  • I updated my answer to show that you can pass a prompt to 'less' to tell the user what to do, and it has other configurable options. They don't have to know it isn't your program and it gives them a lot of flexibility. – clearlight Jan 30 '17 at 04:19
  • @hego64-- I added some comments and a link about using `fflush(stdin)` to my answer. Flushing input streams is undefined behavior in the C Standard, is not portable (e.g., won't work on Linux systems), and is the wrong way to do this. – ad absurdum Jan 30 '17 at 15:18

5 Answers5

3

When getchar() reads a character of user input from the keyboard, there may be extra characters left in the input stream. You need to discard these extra characters before attempting to read again.

I had not noticed the call to scanf() at first (your program would benefit from some whitespace to improve readability), but scanf() will leave characters in the input stream as well. It is always tricky to combine scanf() and getchar() for this reason, and the best solution may be to use fgets() for all user input.

But, to avoid changing your input code too much, you can stick with scanf() and getchar(). Since you need to clear the input stream after both scanf() and getchar(), it might be best to write a function clear_stream():

void clear_stream(void)
{
    int c;
    while ((c = getchar()) != '\n' && c != EOF) {
        continue;
    }
}

This can be incorporated into the function get_single_char(), and also used alone after calls to scanf(), to clear the input stream. There must be at least one character in the input stream when you call clear_stream(), or the function will wait for more input. Using fflush(stdin) to clear the input stream is explicitly not allowed in the C Standard (C11 §7.21.5.2 2). Windows systems have defined behavior for this, but it is not portable (and won't work on Linux, for example). You can read more about this issue here.

Here is a toy example. Note the use of a width specification in the call to scanf() to avoid buffer overflows.

#include <stdio.h>

int get_single_char(void);
void clear_stream(void);

int main(void)
{
    int choice;
    char name[100];

    printf("Enter name: ");
    scanf("%99s", name);
    clear_stream();

    printf("Play a game, %s (y/n)? ", name);
    choice = get_single_char();

    if (choice == 'y') {
        printf("Press ENTER to continue:");
        get_single_char();
    } else {
        puts("That was uninspiring!");
    }

    puts("bye");    
    return 0;
}

int get_single_char(void)
{
    int input;

    input = getchar();

    if (input != '\n') {
        clear_stream();
    }

    return input;
}

void clear_stream(void)
{
    int c;
    while ((c = getchar()) != '\n' && c != EOF) {
        continue;                   // discard extra characters
    }
}
Community
  • 1
  • 1
ad absurdum
  • 19,498
  • 5
  • 37
  • 60
2

At this line scanf("%s",playername); user usually press Enter to mark the end of input, but scanf is reading only to the new line symbol (Enter).

Next, at the line while (enter != '\r' && enter != '\n') { enter = getchar(); } //For Enter getchar is sucking that new-line-char from the previous input.

As you do not change the enter variable before the new input, you still stuck to read the new-line-char.

As a result, entering the name making your code to run without stops.

Try to add fflush(stdin); instruction after any input operation. And remember to reset the enter variable.

Lionishy
  • 188
  • 1
  • 2
  • 10
1

This example using popen() to write to less command running as a subprocess, might save you a lot of trouble and give the user a better experience, depending on the situation, because with less they can scroll back and forth.

Depends on what level of control your program needs.

I tested this code. It works.

UPDATE: I added pclose() so it will close the fd so less knows the input stream is complete, and pclose() will wait for the subprocess running less to terminal (when user presses q).

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/stat.h>

int main(void)
{
        FILE *lessfp;
        if ((lessfp = popen("less -P\"[SPACE] next page, [u] prev page, [q] done:\"", "w")) == NULL) {
                perror("popen");
                exit(-1);
        }

        printf("writing data\n");

        for (int i = 0; i < 1000; i++) {
            fprintf(lessfp, "This is some stuff %d\n", i++);
        }

        pclose(lessfp);

        printf("less exited!\n");
}
clearlight
  • 12,255
  • 11
  • 57
  • 75
  • @DavidBowling I realized after adding the answer it might be a bit out of scope, but hopefully it will help someone else who sees the question and needs a solution like this. Thanks! – clearlight Jan 30 '17 at 05:19
0

Use getc() for the pause. Discard extra characters after the first scanf() and following each call to getc() for the pauses. As David Bowling mentioned, don't fflush(stdin) as it results in undefined behaviour.

See this related post.

...
scanf("%s",playername);

/* discard extra chars */
while((c= getchar()) != '\n' && c != EOF);

printf("Story Text\n");
printf("Story Text.\n");
printf("Story Text\n");
printf("Story Text\n");

/* pause */
printf("Press enter to continue\n");
getc(stdin);
while((c= getchar()) != '\n' && c != EOF);

printf("Story Text\n");
printf("Story Text\n");
printf("Story Text");
printf("Story Text \n");
printf("Story Text\n");
printf("Story Text\n");
...
Community
  • 1
  • 1
Tanner
  • 430
  • 1
  • 5
  • 12
  • This does the same thing that it was doing before. All it does is display the complete text wall. Same as simply using `getchar()` like @David Bowling had said. – hego64 Jan 30 '17 at 04:14
  • Gotchya. I've edited my answer. See this question: http://stackoverflow.com/q/22226529/2281645, and as others have said, don't `fflush(stdin)`. – Tanner Jan 30 '17 at 05:17
  • There is still a problem here.... If the user presses ENTER at the `Press enter to continue` prompt, the newline is consumed by `getc()`, and the next call to `getchar()` in the loop will wait for more input. This is one of the reasons that I wrote a separate function to get a single character: if the character is a `\n` you don't want to start the loop. You can get past this by removing the `getc()` that precedes the loop. The input stream is already clear, and so the loop will continue until the user presses ENTER. – ad absurdum Jan 30 '17 at 06:29
0

Using scanf() consecutive will make an issue of buffer input. For window user, one should use fflush(stdin) but for Linux, it wouldn't work. So Linux user has to repeat the first scanf() before the second scanf() instead of fflush(stdin). Here is my program code:

/* Driver program to test above functions*/
int main()
{
  /* Start with the empty list */
  struct Node* head = NULL;
  char decision = 'y';
  int count=1, val;
  while(decision=='y'){
  printf("Input the data for node %d ",count);
  scanf("%d", &val); <--------------------------------------first scanf()
  insertAtFirstLocation(&head, val);
 
  printf("\nEnter more <y>es/<n>o ? "); 
  //used instead of fflush(stdin) to refresh input buffer
  scanf("%d", &val); <--------------------------------------repeated to refresh
  scanf("%c", &decision); <---------------------------------second scanf() for new input
  count++;
  }
  printList(head);
 
  return 0;
}
Artaghal
  • 21
  • 5