runners3431
runners3431

Reputation: 1455

Putchar and Getchar outputs everytime I hit enter

So I'm doing the exercises in the C Programming Language 2nd Edition by Ritchie/Kernighan. Specially exercise 1.9. "Write a program to copy it's input to output, replacing each string by one more blanks by a single blank."

I believe my solution is correct but everytime I hit enter the screen outputs my line instead of waiting for EOF. Is this the expected behavior? I'm only using the functions and keywords that he has gone over in the book.

If i have multiple newlines, how do I force it to wait until I enter eof before i get output? Putchar just handles it one character at a time right so maybe it's not possible.

Also what is a backspace? Is he refering to just a blank space by spacebar?

/*
 Copys its input to its output, replacing each string with multiple blanks with one. 
 Input  - "I am    running." 
 Output - "I am running."

 */
#include "stdafx.h"


int _tmain(int argc, _TCHAR* argv[])
{
    for (int c = getchar(); c != EOF; ){

        while (c == ' '){
            c = getchar();


            if (c != ' '){
                putchar(' ');
            }
        }

        putchar(c);
        c = getchar();
    }

    return 0;
}

Improved verision:

int _tmain(int argc, _TCHAR* argv[])
{

    int c;

    while ((c = getchar()) != EOF){

        while (c == ' '){
            c = getchar();
                if (c == EOF){
                break;
            }

            if (c != ' '){
                putchar(' ');
            }
        }

        putchar(c);
    }

    return 0;
}

Upvotes: 1

Views: 1420

Answers (5)

David C. Rankin
David C. Rankin

Reputation: 84642

There are a number of ways to approach this problem. As others have correctly shown, if you know your input will not exceed a certain size, you can declare a static buffer sufficient to hold your data. However, if you find yourself in the situation where you don't know how much data you need to read, then you can turn to dynamically allocating your buffer which provides you the ability to grow (or realloc) the size of your buffer as needed. The following provides that approach. (you can redirect a textfile to it as a test).

Also, it was unclear, if you wanted to skip all blank lines, or if you just didn't want to see input until EOF. At any rate, if you wanted to skip blank lines you can add a simple test inside the read loop after you assign p = line;.

/* skip blank lines */
if (*p == '\n' || *p == '\r')
    continue;

This example uses getline instead of fgets which provides 2 advantages for you. (1) getline will allocate the line-buffer for you if you initialize it to NULL; and (2) it returns the actual number of characters read, eliminating the need to call strlen. Let me know if you have questions:

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

void *realloc_void_on (void *p, size_t o, size_t n, size_t psz);

int main (void) {

    char *line = NULL;      /* pointer to use with getline ()   */
    char *p = NULL;         /* pointer to parse getline return  */
    ssize_t nchr = 0;       /* actual chars read per-line       */
    size_t n = 0;           /* max chars to read (0 - no limit) */
    char *buff = NULL;      /* buffer to hold all lines read    */
    char *ep = NULL;        /* end pointer for buff             */
    size_t cbuffsz = 0;     /* current buffer size              */
    size_t nbuffsz = 0;     /* new buffer size                  */
    size_t offset = 0;      /* offset to orient end pointer     */

    /* read each line from stdin */
    while ((nchr = getline (&line, &n, stdin)) != -1)
    {
        p = line;           /* assign line address to pointer   */

        /* set required new buff size, realloc, set end ptr, update cbuffsz */
        nbuffsz += nchr + 1;
        buff = realloc_void_on (buff, cbuffsz, nbuffsz, sizeof *buff);
        ep = buff + offset;
        cbuffsz = nbuffsz;

        while (*p) {                            /* for each character in line       */
            *ep++ = *p;                         /* add char to buff, increment ep   */
            offset++;                           /* increment offset                 */
            if (*p == '\t' || *p == ' ') {      /* if space, or tab                 */
                while (*p == '\t' || *p == ' ') /* read/discard following spaces    */
                    p++;
            }
            else
                p++;                            /* if not space, increment pointer  */
        }
    }

    /* output complete buffer */
#ifdef DEBUG
    printf ("\nBuffer:\n-----\n%s-----\n\n", buff);
#else
    printf ("%s\n", buff);
#endif

    /* free allocated memory */
    if (line) free (line);
    if (buff) free (buff);

    return 0;
}

/* reallocate memory for p of type size psz, from o to n.
* accepts any pointer p, with current allocation o,
* with the type size psz and reallocates memory to
* n, intializing the new memory to zero and returning
* a pointer to the newly allocated block of memory on
* success, exit otherwise.
*/
void *realloc_void_on (void *p, size_t o, size_t n, size_t psz)
{
    void *tmp = realloc (p, n * psz);
#ifdef DEBUG
    printf ("\n  reallocating %zu to %zu\n", o, n);
#endif
    if (!tmp) {
        fprintf (stderr, "Error: pointer reallocation failure.\n");
        exit (EXIT_FAILURE);
    }
    p = tmp;
    memset (p + o, 0, (n - o) * psz);   /* memset new ptrs 0 */

    return p;
}

Build/Compile

Without realloc DEBUG info:

gcc -Wall -Wextra -o buildbuf buildbuf.c

With realloc DEBUG info (including gdb debug info):

gcc -Wall -Wextra -g -DDEBUG -o buildbuf buildbuf.c

Input

I am    running.

Output

I am running.

Upvotes: 0

Arjun Sreedharan
Arjun Sreedharan

Reputation: 11453

Your putchar() works the way you wish because stdin is line buffered, which is not something you should take for granted. You would be better off collecting the strings, parsing them and giving them as output.

Here's what I would do:

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

#define BUFF_SIZE 2048

int main(void)
{

    char buff[2048];
    char *buff_temp = buff;
    char *buff_ptr = buff;
    unsigned int len = 0;

    while (fgets(buff_ptr, sizeof buff, stdin) != NULL) {
        len += strlen(buff_ptr) + 1;
        buff_ptr += strlen(buff_ptr);
    }
    buff_ptr = buff;

    while (--len) {
        while (*buff_ptr == ' ' && buff_ptr[1] == ' ') {
            --len;
            ++buff_ptr;
        }

       *buff_temp++ = *buff_ptr++;
    }
    printf("%s\n", buff);
}

Online compiler link

Upvotes: 1

user3629249
user3629249

Reputation: 16540

backspace is a character, produced by the keyboard (usually there is a key that say 'backspace')

when using 'cooked' input, the backspace is very handy for editing an input line before passing it to the program. (it gets passed to the program when enter/carriageReturn is hit.)

Normally, when text is being entered from the keyhboard, EOF will never occur. However, EOF can be forced from the keyboard, (depending on the OS) either ctrl-z or ctrl-d

Upvotes: 0

Matt
Matt

Reputation: 744

Take a look at theory-behind-getchar-and-putchar-functions it explains why you are getting output after each line.

To force the program to wait for EOF before outputing, you will need to use a buffer of some kind, take a look at print-multiple-lines-by-getchar-and-putchar

Upvotes: 1

John Bollinger
John Bollinger

Reputation: 181724

Your program is not correct for collecting all input until EOF, and then echoing it all back at once. Strictly speaking, it's not even correct for collecting and echoing input line by line -- you get that result only because your standard input is line buffered, which is common, but not required. What it actually does is a character-by-character echo.

Instead, you need to record the input characters in some kind of holding space until you see the EOF, and then echo back the whole contents of that space. With no constraints at all on the form or size of the input, the simplest way to implement it would probably be to buffer the input in a temporary file until the time comes to echo it back. You could also buffer the data in memory, but you do then need to be careful about reserving enough memory and / or dynamically increasing the size of the buffer at need.

"Backspace" is the code generated by the backspace key (not the spacebar), which typically is interpreted as a signal to delete the character immediately preceding the input position. In ASCII, that is character 8 (decimal).

Upvotes: 0

Related Questions