3

I have intervals written as <a;b> which I read with scanf("<%d;%d>", &a, &b);, but the problem is I need to use my function for many such intervals until I press CTRL + Z or CTRL + D on Unix.

So far I tried something like this, but it doesn't work.

int main( void ) {
int a=0, b=0;

printf("Intervals:\n");
while(scanf("<%d;%d>", &a, &b) == 2 && !feof(stdin)){

    printf("Distinct rectangular cuboids: %d\n", capacities(a, b));

}
return 0;
Petr Skocik
  • 58,047
  • 6
  • 95
  • 142

2 Answers2

2

You aren't concise with your problem. This:

int main( void ) { 
    int a=0, b=0;

    printf("Intervaly: \n");
    while(scanf("<%d;%d>",&a,&b) == 2){ 
        printf("Ruznych kvadru: %d\n", capacity(a, b));
    }   
    return 0;  
}

does in fact what you want, either with a long line of <%d;%d> or line by line - both will work. Your problem, hidden in the comments, that an EOF does not terminate this while immediately - rather you need to CTRL-D twice to stop it. The reason for that is also in the comments.

Once you scanf the way you do, you are going to be left with a \n in the input stream (because you have to press return to send your input without terminating). There are two ways to deal with this - add it in the scanf, which is less flexible and has some problems, or clear it in the while:

while(scanf("<%d;%d>",&a,&b) == 2){ 
    printf("Ruznych kvadru: %d\n", capacity(a, b));
    getchar();
}   

You will find now you need to press CTRL-D once. If you tried input redirection from a file you would have seen this problem didn't exist either. Why does it happen? You had \n in the input stream. You press EOF, so scanf gets a newline and ignores it - waiting for new input. Only now a new EOF will kill the scanf.

I suggest searching this site for scanf questions to see why not to use it, and other nice alternatives.

Addendum

A more elegant solution given in the comments and pointed out to me by @BluePixy and pointed out to me by @DavidBowling is to use for your scanf:

scanf("<%d;%d>%*c", &a, &b);

This will naturally gobble up the extra characters, including the new space, without the need for an extra getchar. This I think is the most elegant solution to this scanf usage.

Even more

Following the comments by OP and again by David (thanks again!) in order to account for 0 or more spaces, you just need a space directive in the formatting:

scanf("< %d ; %d >%*c", &a, &b);

Now spaces may (or may not) be added around each integer.

kabanus
  • 24,623
  • 6
  • 41
  • 74
  • "add it in the `scanf`, which is less flexible and has some problems" -- how are you imagining including this in the call to `scanf()`, and what are the problems? – ad absurdum Nov 09 '17 at 13:58
  • @DavidBowling `scanf("<%d;%d>\n",&a,&b)` - obviously I can't input multiple coordinates now in one line. It will read the first one, and the buffer will remain full with the rest of the inputs, which will lead to other annoyances. I rather it not appear in the answer. – kabanus Nov 09 '17 at 13:59
  • Using trailing whitespace in `scanf()` format strings is a mistake anyway; this causes `scanf()` to continue matching whitespace characters until a non-whitespace character is encountered, leading to interactivity problems. But what about: [`scanf("<%d;%d>%*c", &a, &b)`](https://stackoverflow.com/questions/47202353/intervals-on-input-until-ctrl-d#comment81353544_47202353)? Multiple inputs are fine on a single line here (so long as there is exactly one character between them; your solution requires the same). – ad absurdum Nov 09 '17 at 14:15
  • @DavidBowling I agree to the first part, and didn't think about the second part - that's a good idea, which I did not consider. Do you want to add it as an answer of your own or add it to this answer? I think it's better than mine if we insist on using `scanf`, as it naturally gobbles the newline. The only disadvantage is it won't break on a bad character - so this would have to be decided by what OP needs. – kabanus Nov 09 '17 at 14:21
  • I take that back, it has every advantage, kudos for thinking of this. – kabanus Nov 09 '17 at 14:28
  • This was the suggestion made by @BLUEPIXY in the comments; feel free to add it to your answer, if you like. – ad absurdum Nov 09 '17 at 14:43
  • Well, I found another problem with that. Interval can also be wrote like `< a ; b >` with spaces – Pavel Straka Nov 09 '17 at 21:03
  • @PavelStraka The problem is what you're trying to do with `scanf`. There is no way I know of to allow either a space or no space in the middle of a scan. You can't do this with `scanf` in one go. Better off using `getchar` to read character by character. – kabanus Nov 10 '17 at 11:19
  • @kabanus -- that is not true. A space directive in the format string tells `scanf()` to match zero or more whitespace characters. All that is needed are three additional spaces (one at the beginning and two after the `d` specifiers): `while(scanf(" <%d ;%d >*c",&a,&b) == 2) {}`. Since `%d` automatically skips leading whitespace, this allows arbitrary spaces before, after, and in the middle of the input. Also, I love David Bowie, but my name is David _Bowling_ ;) – ad absurdum Nov 10 '17 at 19:46
  • @DavidBowling I didn't know that, I was picturing a `*[ ]` construct. Added to answer. Also spell check upgraded you to super star I guess :) – kabanus Nov 11 '17 at 00:23
  • Nothing wrong with what you have, except you may want to put a leading space in the format string to take care of any extra trailing spaces from previous input. Technically, you don't need the two spaces in front of `%d`, since this _automatically_ skips over whitespace. The minimum requirement is: `" <%d ;%d >*c"`. That is, one space before `<`, and one space after each `%d`. BTW, this would accept input such as `< 1 ; 2 >x`, and then terminate the loop. Also, my mom thinks I am a superstar; glad to hear that auto-correct agrees ;) – ad absurdum Nov 11 '17 at 00:35
0

As for the loop condition, while( scanf(" <%d;%d>", &a, &b)) == 2 ) should be enough, but you should probably also keep track of the number for error diagnosis:

int nread=2;
while( (nread=scanf(" <%d;%d>", &a, &b)) == 2){ //...

The Ctrl-Z part is more complicated. Ctrl-Z causes canonically set terminal drivers to generate the SIGTSTP signal.

The default disposition of this signal is to put the process to sleep, which means you can't simply use ISO C's signal function to register a signal handler, because these signal handlers may inadvertently restore the original signal disposition if more than one instance of the signal is received, which would put your process to a halt.

You need to reach into POSIX and use sigaction.

The sigaction handler can then set a volatile flag, which will be a signal to terminate the loop. Since the reception of the signal while inside scanf will cause scanf to fail with EINTR, robust error handling for this is a little convoluted:

#include <stdio.h>
#include <signal.h>
#include <errno.h>

static int volatile ctrl_z;
void SignalHandler( int Signum )
{
    (void)Signum;
    ctrl_z=1;
}

int main( void ) {
    // ctrl + z
    sigaction(SIGTSTP, &(struct sigaction){ .sa_handler=SignalHandler }, 0);

    int a=0, b=0, nread=2;

    /*...*/
    while( (nread=scanf(" <%d;%d>", &a, &b)) == 2){

       /*...*/

        if(ctrl_z)
            return 0;
        errno=0; /*so we know if scanf failed because of the signal*/
    }
    if ( (!ferror(stdin) && feof(stdin)) ||
           (ferror(stdin) && errno==EINTR && ctrl_z) )
        return 0;
    return 1;
}
Petr Skocik
  • 58,047
  • 6
  • 95
  • 142