0

I have been given an assignment where I shall write a program to read a string until the user enters $. When the user enters $ reading the message should stop then program should go to the next step which is some encoding operation on the read string.
Additional constraints:

Using the library functions of <conio.h> is not allowed in the assignment.

Is there any way to do this with the gets or the scanf function?

****Edit:** By entering $ compiler should stop reading and should go to next command. so user will not be able to enter any other text.**

  • `getch`? What's not allowed? – Arc676 Nov 27 '15 at 08:29
  • conio.h. so getch is not allowed –  Nov 27 '15 at 08:32
  • The other way is to employ `scanf` here with format string like `%[^$]` – Sergey Kanaev Nov 27 '15 at 08:35
  • but how? can you explain more? –  Nov 27 '15 at 08:36
  • [man fgets](http://linux.die.net/man/3/fgets). – Lundin Nov 27 '15 at 08:37
  • 1
    Why don't you try it out yourself? Please post what you have tried so far, because this sounds a lot like a homework question.. :/ – Zimano Nov 27 '15 at 08:39
  • 2
    See [Why the `gets()` function is too dangerous to be used](http://stackoverflow.com/questions/1694036/why-is-the-gets-function-dangerous-why-should-it-not-be-used) for reasons why you should not even think of using `gets()` — you might use `fgets()`, but it probably isn't appropriate. Unless you jigger the input system, you won't get to see what the user types until they hit return at the end of a line. – Jonathan Leffler Nov 27 '15 at 08:41

4 Answers4

3

I don't think ISO C provides facilities for changing stdin from line buffered to unbuffered (at the OS tty level, not the C stdio buffering level), so input only becomes available when the user presses return.

If you can use POSIX functions, then you can change the terminal to raw mode. Then it becomes an OS-specific question, not a C question. And of course, Windows is going to be different from a POSIX OS, unless you're using cygwin or maybe mingw.

Other than that, you can do what David Rankin suggested in comments, and loop on reading a character at a time.

Peter Cordes
  • 328,167
  • 45
  • 605
  • 847
2

Keyboard in raw-unbufferd mode

As noted by Peter, you can accomplish what you apparently desire, that being stopping input immediately upon the keypress of '$' without conio.h, but it requires setting the terminal in raw-unbuffered mode. In essence you are changing the way the terminal responds to a keypress.

Rather than buffering the character in stdin until a newline is read, in raw-unbuffered mode, each character is immediately available to test which allows you to check each for your sentinel character and continue when it is read. Since you are changing the way the terminal responds to each keypress, you must reset the normal behavior when you are done collecting input.

There are a couple of ways to check for the availability of a character in stdin. One of the easiest is to use select to test the file descriptor and return a character when it becomes available. A (relatively short) example would be:

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

#include <sys/time.h>
#include <termios.h>
#include <errno.h>   /* for errno */
#include <unistd.h>  /* for EINTR */

#define MAXC 256

static struct termios g_old_kbd_mode;

/* kbhit uses select to check for input on stdin. */
static int kbhit (void){
    struct timeval timeout;
    fd_set read_handles;
    int status;

    /* check stdin (fd 0) for activity */
    FD_ZERO (&read_handles);
    FD_SET (0, &read_handles);

    timeout.tv_sec = 0;   /* timeout 0 forces select to return immediately, if  */
    timeout.tv_usec = 0;  /* NULL, select blocks indefinitely.                  */

    do {
        status = select (0 + 1, &read_handles, NULL, NULL, &timeout);
    } while (status == -1 && errno == EINTR);
    return status;
}

/* restore original keyboard attributes */
static void kbd_old_attr (void){
    tcsetattr (0, TCSANOW, &g_old_kbd_mode);
}

int main (void) {

    char buf[MAXC] = {0};
    char *p = buf;
    int c;
    static char init;
    struct termios new_kbd_mode;

    if (init) return 1;

    /* put keyboard (stdin, actually) in raw, unbuffered mode */
    tcgetattr (0, &g_old_kbd_mode);
    memcpy (&new_kbd_mode, &g_old_kbd_mode, sizeof(struct termios));

    new_kbd_mode.c_lflag &= ~(ICANON);
    new_kbd_mode.c_cc[VTIME] = 0;
    new_kbd_mode.c_cc[VMIN] = 1;
    tcsetattr (0, TCSANOW, &new_kbd_mode);

    printf ("\n enter text ('$' ends): ");
    while (!kbhit()) {          /* this only works with timeout 0   */
        c = getchar();          /* get keypress from user */
        if (c == '$') break;    /* test kepress for '$'   */
        *p++ = c;               /* assign char to buf     */
        if (p-buf >= MAXC-1) {  /* check against limit    */
            fprintf (stderr, "warning: character limit reached.\n");
            break;
        }
    }
    *p = 0;             /* null-terminate */
    kbd_old_attr ();    /* restore original attributes  */

    printf ("\n text entered : %s\n\n", buf);

    return 0;
}

Use/Output

$ ./bin/keypress_dollar

 enter text ('$' ends): abcdefgjik$
 text entered : abcdefgjik

Note: In this example while (!kbhit) requires select to return 0. Using a timeout of 0 forces select to return immediately with a char available in fd0 leaving 0 file descriptors remaining. Since the return value for select is the number of descriptors remaining in the set -- it works in this limited case. To use with a timeout, or NULL, you must modify the caller so it is not within while (...).


Original -- User Required to Press Enter

You can use either getchar or scanf to read a string up until you reach a sentinel character such as '$'. One way using getchar would be:

#include <stdio.h>

#define MAXC 256

int main (void) {

    char buf[MAXC] = {0};
    int i = 0, c;

    printf ("\n enter string  : ");
    while (i + 1 < MAXC && (c = getchar()) != '$' && c != '\n' && c != EOF)
        buf[i++] = c;
    buf[i] = 0;     /* null-terminate */

    printf (" string entered: %s\n\n", buf);

    return 0;
}

Example/Output

$ ./bin/getchar_dollar

 enter string  : a quick brown fox jumps over$ the lazy dog.
 string entered: a quick brown fox jumps over

Note: all characters following the '$' remain in the input buffer (e.g. stdin), so you would need to empty stdin before your next input/read operation or your would read the characters that are left as your input.

David C. Rankin
  • 81,885
  • 6
  • 58
  • 85
0

Try this as a starting point:

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

    void read_input(char buffer[]) 
    {
        char character;
        int i = 0;
        do
        {
            character = getchar(); 
            buffer[i] = character; 
            ++i;
        }
        while ( '$' != character );
        buffer[i - 1] = '\0'; 
    }

    int main (void)
    {
        char input[100] = "";
        printf("Input message: ");
        read_input(input);
        printf("Input = %s\n", input); 
        return 0;
    } 
artm
  • 17,291
  • 6
  • 38
  • 54
  • I want the compiler to go to next level by entering $. so user should not be allowed to write text after entering $. –  Nov 27 '15 at 08:57
  • I don't think you can do that though. C is _buffer input_, ie. user can enter anything and the program will not interpret until user enter `Enter` – artm Nov 27 '15 at 09:03
0

Try this logic. Its not a complete C code. But still you can get the concept from this, I hope.

#define BUF_SIZE 1024
/*=====================*/
char msg[BUF_SIZE], ch;
int count = 0;
do
{
    scanf("%c", &ch);
    if(ch == '$' || count == BUF_SIZE - 1)
    {
         msg[count] = '\0';
         break;
    }
    else
    {
         msg[count] = ch;
    }
    count++;   
}
while(1)