-2

I recently started learning C from The C Programming Language by Brian Kernighan and Dennis Ritchie. In that book, there's a whole subsection (1.5.1, 2nd Ed.) where the authors create a file copying program (but I am using this as a text (input by the user) copying program). Their code basically looks like this

#include <stdio.h>

int main()

{
    int c;
    c = getchar();

    while (c != EOF) {
        putchar(c);
        c = getchar();
    }
}

This code works fine when run, but the program never stops. The program keeps on waiting for inputs and goes on copying them, endlessly, never terminating. Now I wanted to create a program where this endless copying does terminate. To do so, I tweaked the code a little bit. Here's my code

#include <stdio.h>

int main()

{
    int c;
    c = getchar();

    while (c != EOF) {
        putchar(c);
        c = getchar();
    }

    return 0;

}

I thought that explicitly writing return 0; at the end might do the job, but it seems that the output doesn't change at all. I also tried using if-else conditions and looping them as follows

#include <stdio.h>

int main()

{
    int c;
    c = getchar();

    while (1) {

        if (c != EOF){
            putchar(c);
            c = getchar();
        }

    else
        return 0;
    }
}

But the program didn't change at all in this case as well.

My question is that how do I change the code so that my program ends just after copying once (or, even better, generalising it to copying n times)?

Here, it is important for me to clarify what I mean by "once" (or "n times" for that matter). When I use the command prompt (I use Windows) to run the compiled file, the program expects an input. I enter the input through my keyboard, and then press Enter to see the reproduced input. After this, the program again expects me to enter some input. I don't want this to happen. I want the program to end by itself after it copies my input once. This is what I mean by "once" (and the above definition can easily be extended for "n times" as well).


@anatolyg and @Jabberwocky have suggested the use of \n (new line escape character) to make the program work. But this solution fails when the input contains linebreaks, for example

my code does
not work so

I am asking
for help here

Copying this into the command prompt as the input, and using the program suggested by the above two users yields only my code does as the output, which is not what I wanted as an output. Is there any way I can make this program work for text blocks with linebreaks and also make it stop after one input (in this case, the whole text block is just one input)?


After interacting with the other people here, I have come to realise that my definition of "one input" or "once" is quite vague, unclear and not at all concrete. Thus it is nothing but expected that C cannot do what I want it to do. I am accepting the answer which suggests to use the new-line escape character as a signal to terminate the program, because that is the closest to what I had in my mind. Thank you all.

FakeMod
  • 125
  • 1
  • 7
  • 1
    How are you entering 'EOF'? – matt Oct 26 '20 at 07:44
  • @matt I am sorry, but I am not able to understand your question. Would you please elaborate? If you're asking about entering `EOF` as an input, then I am using Windows and thus I use 'Ctrl + Z' to enter `EOF` as an input. – FakeMod Oct 26 '20 at 07:48
  • 1
    It looks like "just after copying once" is important for you, and you have some special definition of "once". Typically, "reading the file once" means stopping at end-of-file, which is exactly what your program does. Please clarify what "once" means for you. You can [edit] your post to explain that. – anatolyg Oct 26 '20 at 07:53
  • @anatolyg Thank you for pointing that out. I did edit my answer to include what I meant by "once". – FakeMod Oct 26 '20 at 07:59
  • 2
    @FakeMod and how do you think the program could know when it should end? It's absolutely unclear what you want. – Jabberwocky Oct 26 '20 at 08:30
  • @Jabberwocky Well, in my mind, it should end once I have given my first input, i.e. it has copied, and is now at the end of the input. – FakeMod Oct 26 '20 at 08:32
  • @FakeMod sorry I don't understand. First you want to have one line and the next time you want several lines.... I give up. – Jabberwocky Oct 26 '20 at 08:33
  • 1
    The thing is that the computer can't know what "first input" means. It seems that you've defined it as "one paste operation from the clipboard" (ctrl-z) but the program can't know if the text is coming from the clipboard or from the keyboard so you can't have that kind of feature. – Guy Incognito Oct 26 '20 at 08:34
  • Sorry to hear that @Jabberwocky, but I am starting to realize that it is not possible for the program to identify what I mean by "once". Thank you for your answer. – FakeMod Oct 26 '20 at 08:36
  • If you *only* do copy-paste to this program then you could try something like ending the loop for example 0.5 seconds after the first input so it would have time to get everything you paste and then continue, but then it wouldn't work for normal keyboard input. – Guy Incognito Oct 26 '20 at 08:36
  • @GuyIncognito *Response to the first comment:* That seems to clear it up for me thanks. *Response to the second comment:* Yes, that might be a nice workaround, but it seems like an overkill to me. I guess I'll stick with Ctrl + Z :) – FakeMod Oct 26 '20 at 08:38
  • 1
    @FakeMod also, you should be able to read to EOF *several* times. After hitting EOF, you will use [clearerr](http://port70.net/~nsz/c/c11/n1570.html#7.21.10.1) to clear the EOF and error indicator**s**, then you can loop again. Also, some "paste-detecting" code is possible - if you get several lines in rapid succession then certainly the user did not paste them, as even a speediest typist would not probably write a line per second. – Antti Haapala -- Слава Україні Oct 26 '20 at 09:01

6 Answers6

5

Your return 0 won't make any difference as the execution will stay in the while until c becomes EOF. So you need to make c equal to EOF. Try ctrl-D or ctrl-Z depending on OS.

My question is that how do I change the code so that my program ends just after copying once (or, even better, generalising it to copying n times)?

Introduce a counter like:

#define MAX_INPUTS 10

...

int cnt = 1;
while (c != EOF && cnt < MAX_INPUTS) {
    putchar(c);
    c = getchar();
    ++cnt;
}
Support Ukraine
  • 42,271
  • 4
  • 38
  • 63
  • Yes, I can terminate the program by either pressing 'Ctrl + C' (works everywhere) or by 'Ctrl + Z' (works here only), but that's not what I want to do. I want the program to end by itself after copying once. – FakeMod Oct 26 '20 at 07:50
  • @FakeMod `Ctrl+C` is something different. It terminates the program. `Ctrl-Z` or `Ctrl-D` should feed `EOF` in `stdin` making the program terminate on its own. – Gerhardh Oct 26 '20 at 07:56
  • @FakeMod, the problem is “EOF” signifies “end of file”, and yet your program is reading from your terminal instead of a file, so there is no end. You simulate the end with the suggestions given here (for you, CTRL+Z). – Elliott Oct 26 '20 at 07:58
  • @Elliott Yes, EOF does signify "end of file". Is there any way I can rather use "end of input" in criteria? – FakeMod Oct 26 '20 at 08:01
  • @FakeMod, How would your program know when the inputs were ended? C was designed to be modular. It sees your inputs as a file. Think of it as having no idea that its inputs are coming from stdin (the terminal), instead learn to be comfortable with ctrl+d or ctrl+z to tell it to stop. It’s not a hacky solution. It’s how it’s supposed to work. – Elliott Oct 26 '20 at 08:04
  • @Elliott Oh, I see. That does sound like an answer that I can accept. Thank you. – FakeMod Oct 26 '20 at 08:30
2

For starters this loop

while (c != EOF) {
    putchar(c);
    c = getchar();
}

is not endless. There is a condition in the while loop that if it is not satisfied the loop terminates.

To generate the output of the function getchar equal to EOF you should enter the key combination Ctrl + c in Windows or Ctrl + d in an Unix system.

Adding a return statement either in the body of the loop or after the loop does not principally influence on the program control flow.

Pay attention to that the return statement in main may be omitted. From the C Standard (5.1.2.2.3 Program termination)

1 If the return type of the main function is a type compatible with int, a return from the initial call to the main function is equivalent to calling the exit function with the value returned by the main function as its argument;11) reaching the } that terminates the main function returns a value of 0.

So for example these two programs

int main( void )
{
    return 0;
}

and

int main( void )
{
}

are valid and equivalent.

Vlad from Moscow
  • 301,070
  • 26
  • 186
  • 335
1

This code works fine when run, but the program never stops.

This is not true, if you don't satisfy the condition of the while statement:

while (c != EOF)

the program will terminate. Now how you can send EOF to the input?

Depends. Which stdin operation are you trying to EOF? If stdin is terminalinput just press control-D. If the ONLY thing you want to send is EOF, use echo -n | (your program). If you are sending it FROM your C program, you are pretty much stuck with exiting from the program, at which point the state of its stdout will be EOF, which will in turn be reflected in whichever stdin it was redirected to. I'm pretty sure you can't just send EOF from within C without getting a hold of an underlying buffer, which aren't standardized, and therefore would leave you with some dirty programming to do.

Another solution is check in or some digitable character in the while condition for example:

#include <stdio.h>

int main()

{
    int c;
    c = getchar();

    while (c != EOF && c!= 'A') {
        putchar(c);
        c = getchar();
    }
}

in this case if you digit "A" the program will ends up.

Zig Razor
  • 3,381
  • 2
  • 15
  • 35
1

Ypur latest edit suggests you might want something like this:

#include <stdio.h>

int main()    
{
  int c;
  c = getchar();

  while (1) {
    if (c != EOF && c != '\n') {  // '\n' is End of line (Enter)
      putchar(c);
      c = getchar();
    }

    else
      return 0;
  }
}

or simpler:

int main()
{
  do
  {
    int c = getchar();

    if (c != EOF && c != '\n') {  // '\n' is End of line (Enter)
      putchar(c);
    }
    else
    {
      return 0;
    }
  } while (1);
}
Jabberwocky
  • 48,281
  • 17
  • 65
  • 115
  • Thank you for the solution, but this solution fails, or at least doesn't work as intended, in cases involving linebreaks. For example, if I intend to copy the following text block (linebreaks don't work here so I am also including this text block in my question): "my code does[Enter] not work so[Enter][Enter] I am asking[Enter] for help here" the program only copies the first line (as expected). Thus it "fails" in such cases. I don't want that to happen. Is there any way I can retain the feature of copying multi-line text and also quit the program after a single input? – FakeMod Oct 26 '20 at 08:10
  • @FakeMod it think you need to understand what EOF means. Actually the 3rd version of the progtam in your question does exactly what you want. You can enter multiple lines, each line is echoed, but you need to tell the program that the input ends at some time and theredore you need to press Ctrl+Z and the Enter. Anyway It's really unclear what you want. Edit your question and add some examples of input/output. – Jabberwocky Oct 26 '20 at 08:16
  • Please see my edit (though it is just equivalent to my first comment). As for the last part of your comment, I am in the process of making my criteria more clear (by examples and better phrasing), thank you. – FakeMod Oct 26 '20 at 08:28
1

Since you want to read only one line of input, you can terminate the loop on the end-of-line character '\n'.

c = getchar();

while (c != '\n') {
    putchar(c);
    c = getchar();
}

If you use a Windows terminal to enter data to your program, this will be enough. If you redirect the input to your program, so that it comes from a file, it will read the file up to the first newline character. However, if the file contains no newline character, your program will get EOF at end of file. So the terminating condition should also check for EOF.

c = getchar();

while (c != '\n' && c != EOF) {
    putchar(c);
    c = getchar();
}

If you want to read the first n lines from the file/terminal, you need a counter. Then your code will become a bit more complicated, and I think an endless loop with exits in the middle would be a better implementation.

int line_counter = 0;
int max_lines = 5;

while (1) {
    int c = getchar();
    if (c == EOF)
        break;

    putchar(c);

    if (c == '\n')
    {
        ++line_counter;
        if (line_counter == max_lines)
            break;
    }
}

Another method for separating between inputs is with an empty line. This redefines input format to have "paragraphs" of text. This will complicate the code a little more - it should also hold the previous character it read. To detect an empty line, which marks the end of a paragraph: if both previous and current character are end-of-line characters, the paragraph of text has just ended.

int record_counter = 0;
int max_records = 5;
int prev_c = '\n';

while (1) {
    int c = getchar();
    if (c == EOF)
        break;

    putchar(c);

    if (prev_c == '\n' && c == '\n')
    {
        ++record_counter;
        if (record_counter == max_records)
            break;
    }

    prev_c = c;
}
anatolyg
  • 26,506
  • 9
  • 60
  • 134
0

The function getChar() in this case is always waiting for the new character, and in this case c is never to get the EOF value. You can set a character to finish (for example 'e' to exit) or you can count with a counter as you say to exit the loop. Do not use while(1) because is not a good practise.

David
  • 156
  • 4
  • Thanks David. This was helpful, but how do I implement a counter here? I do not know how many characters is my program going to receive in its first input, so I also do not now how many times do I need to repeat the loop. As for your last line, why is using `while(1)` not a good practice? – FakeMod Oct 26 '20 at 07:53
  • Is while(1) really not good practice? how so? I use it all the time. – Elliott Oct 26 '20 at 08:00
  • I think if you don't know how many characters you will receive the best option is to set a escape character, like I said the e character or something. Anyway I show you an example of a counter: int main() { int c; c = getchar(); int counter=0; while (counter<=8) { putchar(c); c = getchar(); counter++; } return 0; } In this case the loop ends after 8 characters. I said the while(1) is not a good practice because you are creating a endless loop, and in programming is important to set a end condition to a loop – David Oct 26 '20 at 08:01
  • To respond Elliot, while(1) is only helpful in programs that are planned to be executed all the time in this same statment of code. It is mostly use in Arduino programming and other devices. But in general, it makes the code harder to understand and if you have to exit the loop, you have to use go to, or break instructions and these are really not good practices. – David Oct 26 '20 at 08:22