7

Relevant code snippet:

char input [1024];

printf("Enter text. Press enter on blank line to exit.\n");
scanf("%[^\n]", input);

That will read the whole line up until the user hits [enter], preventing the user from entering a second line (if they wish).

To exit, they hit [enter] and then [enter] again. So I tried all sorts of while loops, for loops, and if statements around the scanf() involving the new line escape sequence but nothing seems to work.

Any ideas?

Mat
  • 202,337
  • 40
  • 393
  • 406
user688604
  • 71
  • 1
  • 1
  • 2
  • 2
    `scanf()` is hard to use, especially for this. Use `fgets()` instead, it's going to be a lot simpler. – Greg Hewgill Nov 27 '12 at 20:57
  • 1
    @David Please don't mention `gets` as if it were a suitable option for anything. It's too unsafe (and fortunately finally removed from the language). – Daniel Fischer Nov 27 '12 at 20:59
  • This is a school thing. We never learned `fgets()`. The instructions say to read in whole lines of text, and then that we'll have to use `scanf()` to read in a whole line. – user688604 Nov 27 '12 at 21:04
  • @DanielFischer - Unmentioned :) I agree, but wasn't aware it'd been removed. Thanks! – prprcupofcoffee Nov 27 '12 at 21:07
  • @David I hoped you'd just edit it out of your comment, the rest of it was good. – Daniel Fischer Nov 27 '12 at 21:08
  • @user688604: It doesn't matter whether you "learned" `fgets()` or not. The way to read in whole lines of text is to use `fgets()`. – Greg Hewgill Nov 27 '12 at 21:08
  • @DanielFischer - I'd intended to, but I hadn't noticed that IE went into compatibility mode, so the edit link had gone away. – prprcupofcoffee Nov 27 '12 at 21:14
  • @GregHewgill: I understand but they don't expect us to use `fgets()`. The implication is that this is possible with `scanf()`. – user688604 Nov 27 '12 at 21:17
  • Well it's certainly *possible*. Just like it's *possible* to eat soup with a fork - it's the wrong tool for that specific job. – Greg Hewgill Nov 27 '12 at 21:21
  • @GregHewgill: I dunno. It's quite easy to do things like this with scanf() as long as you know how the function works. – tmyklebu Nov 27 '12 at 21:24
  • @GregHewgill `while(scanf("%c", &ch) == 1 && ch != '\n') { input[i++] = ch; if (i + 1 >= sizeof input) break; } input[i] = 0;` Yup, agree. – Daniel Fischer Nov 27 '12 at 21:25
  • @GregHewgill: I agree it's the wrong tool for the job, but it's the assignment. If he ignores the instructions he risks a failing grade. Besides, it's worth knowing the power of scanf() beyond the simplistic formats most commonly used. – Carey Gregory Nov 27 '12 at 22:20
  • @tmyklebu "Easy" and "`scanf`" don't belong in a sentence together (unless also accompanied by "to get wrong"). – jamesdlin Aug 23 '13 at 04:54
  • @jamesdlin: Read the man page. This isn't rocket surgery. – tmyklebu Aug 23 '13 at 06:00
  • @tmyklebu Consulting the man page is not going to help you deal with `scanf`'s many pitfalls. Really, you're better off avoiding it entirely. http://www.c-faq.com/stdio/scanfprobs.html – jamesdlin Aug 23 '13 at 06:21

5 Answers5

6

Try this:

while (1 == scanf("%[^\n]%*c", input)) { /* process input */ }
tmyklebu
  • 13,915
  • 3
  • 28
  • 57
  • 2
    To make this safe, add the size of the input buffer minus 1 to the format specifier. – Carey Gregory Nov 27 '12 at 21:11
  • 1
    This appears to work. Can you explain it a little? What does %*c do and why does it all equal 1? – user688604 Nov 27 '12 at 21:14
  • 1
    @Carey: But then it doesn't work anymore, since the character eaten might be the last character on a line. user688604: %*c eats a character. scanf returns the number of arguments it filled in before it finished or failed. It will fail to match %[^\n] if the first character it sees is a newline. Once it fails, the next character to be read from stdin will be a newline. – tmyklebu Nov 27 '12 at 21:16
  • Thanks @tmyklebu. Very much appreciated. I feel silly bashing my head against this problem for last few hours (yikes!). – user688604 Nov 27 '12 at 21:26
  • 1
    @tmyklebu: Sure it works (I just tested it). The only potential problem is if the user enters more characters than the input buffer can hold, the loop will terminate. But that's a whole lot better problem to have than a buffer overrun. In this example, the format specifier should be "%1023[^\n]%*c". – Carey Gregory Nov 27 '12 at 22:15
  • You didn't test it very well, then. Using %1[^\n]%*c and feeding the code "aa" on stdin causes it to exit prematurely. Code that's wrong is generally worse than code that crashes predictably when its design constraints are violated; just make sure you're reading into a buffer that has an unwritable page after it. – tmyklebu Nov 27 '12 at 22:29
  • 1
    @tmyklebu: That's what I said: "The only potential problem is if the user enters more characters than the input buffer can hold, the loop will terminate." So I tested it perfectly well. I would rather simply handle the error of too much input rather than trying to force my data buffers to reside in special places in memory. – Carey Gregory Nov 28 '12 at 16:40
  • @tmyklebu : What about if I need to use the same code but I need to read something other than a string buffer? Some floats for example *(on multiple lines)*? – user2284570 Mar 25 '15 at 04:09
  • `scanf("%[^\n]%*c", input)` fails to read anything if the first character is a `'\n'`. – chux - Reinstate Monica Jun 13 '22 at 03:40
1

As was yet pointed out, fgets() is better here than scanf().

You can read an entire line with fgets(input, 1024, stdin);
where stdin is the file associated to the standard input (keyboard).
The function fgets() reads every character from the keyboard up to the first new-line character: '\n' (obtained after pressing ENTER key, of course...).
Important: The character '\n' will be part of the array input.

Now, your next step is to verify if all the characters in the array input,
from the first to the '\n', are blanks.
Besides, note that all the characters after the first '\n' in input are garbage, so you have not to check them.

Your program could be as follows:

char input[1024];
printf("Enter text. Press enter on blank line to exit.\n");
while (1) {
   if (fgets(input, 1024, stdin) == NULL)
     printf("Input Error...\n");
   else {
     /* Here we suppose the fgets() has reached a '\n' character... */
     for (char* s = input; (*s != '\n') && isspace(*s); s++)
        ; /* skipping blanks */
     if (*s == '\n')
        break; /* Blank line */
     else
        printf("%s\n", input); /* The input was not a blank line */
   }
}

That code must be written inside your main() block and,
more importantly, it is necessary to include the header <ctype.h> before all,
because the isspace() function is used.
The code is simple: the while is executed for ever, the user enter a line in each iteration, the if sentences checks if some error has happened.
If everything was fine, then a for(;;) statement is executed, which explores the array input to watch if there are just blanks there... or not.
The for iterations continue up to the first new-line '\n' is found, or well, a non-blank character appears.
When for terminates, it means that the last analyzed character, which is held in *s, is a newline (meaning that all earlier characters were blanks), or not (meaning that at least there is some non-blank character in input[], so input is a normal text).

The "ethernal" while(1) is broken only in case that a blank-line is read (see the break statement in 11th line).

pablo1977
  • 4,281
  • 1
  • 15
  • 41
1

OP says "To exit, they hit [enter] and then [enter] again"

unsigned ConsecutiveEnterCount = 0;
for (;;) {
  char buffer[1024];
  if (fgets(buffer, sizeof(buffer), stdin) == NULL) {
    break;  // handle error or EOF
  }
  if (buffer[0] == '\n') {
    ConsecutiveEnterCount++;
    if (ConsecutiveEnterCount >= 2 /* or 1, not clear on OP intent */) {
      break;
    }
  }
  else ConsecutiveEnterCount = 0;
  // Do stuff with buffer;
}
chux - Reinstate Monica
  • 143,097
  • 13
  • 135
  • 256
1
#include <stdio.h>
int main(){
    char arr[40];
    int i;
    for( i = 0; i < sizeof(arr); i +=2 ){
        scanf("%c%c",&arr[i],&arr[i+1]);
        if( arr[i] == '\n' && arr[i+1] == '\n' )
            break;
    }
    printf("%s", arr);
    return 0;    
}
Darkstarone
  • 4,590
  • 8
  • 37
  • 74
  • What if input has an odd number of characters before the '\n' ? Try "a" : arr[i] will contain 'a' and arr[i + 1] will get the '\n'. You will have to type three times (vs two times for an even number of chars, like "ab"). This is just wrong. – Bruno Jan 23 '22 at 18:33
1

... I tried all sorts of while loops, for loops, and if statements around the scanf() involving the new line escape sequence but nothing seems to work.

It seems you tried everything that you shouldn't have tried, prior to reading! A C programmer is expected to read manuals lest they want to run into undefined behaviour which causes headaches like the one you've experienced. To elaborate, you can't learn C by guessing like you can Java.

Consider this your lesson. Stop guessing and start reading (the fscanf manual)!

According to that manual:

[ Matches a non-empty sequence of bytes from a set of expected bytes (the scanset).

The emphasis is mine. What you seem to be describing is an empty sequence of bytes, which means that the match fails. What does the manual say about matching failures?

Upon successful completion, these functions shall return the number of successfully matched and assigned input items; this number can be zero in the event of an early matching failure. If the input ends before the first conversion (if any) has completed, and without a matching failure having occurred, EOF shall be returned. If an error occurs before the first conversion (if any) has completed, and without a matching failure having occurred, EOF shall be returned...

Again, the emphasis is mine... This is telling you that like most other C-standard functions, you need to check the return value! For example, when you call fopen you then write some idiom along the lines of if (fp == NULL) { /* handle error */ }.

Where's your error handling? Note that the return value isn't merely a binary selection; where n conversions are performed, there are n+2 possible return values in the range of: EOF, 0 .. n. You should understand what each of those means, before you try to use fscanf.

autistic
  • 1
  • 3
  • 35
  • 80
  • On a side-note, if you can't be bothered *reading the manual* then I don't know why you're here... Reading the manual is actually *less* effort than asking a question! – autistic May 30 '17 at 06:34