LinuxQuestions.org

LinuxQuestions.org (/questions/)
-   Programming (https://www.linuxquestions.org/questions/programming-9/)
-   -   a C programming question; howto prompt for restart script? (https://www.linuxquestions.org/questions/programming-9/a-c-programming-question%3B-howto-prompt-for-restart-script-4175462524/)

Erik_FL 05-19-2013 10:38 PM

Quote:

Originally Posted by lleb (Post 4954549)
question about this. isnt %d the place holder for integer? isnt %c that place holder? if the answer is going to be a char, why would we be looking for an integer? is that due to ANS[2] or BUFFER[81]?

The reason that the "fgets( )" does not wait for the Y/N answer is a little bit involved to explain.

After typing in the number 1000, the information in "stdin" would look like this, for example.

1000\n

I used "\n" to represent the new-line character. Your program is reading the number like this.

Code:

scanf( "%d", &FAH );
That will leave information in "stdin" like this.

\n

Later, your program wants to read a Y or N answer. When your program calls "fgets" to read a line, or "scanf" to read a character, it will read the new-line character (without waiting). What I was getting at is that the whole not waiting problem is caused by how the program reads the number, since it does not read the entire input line. So it is not enough to just use "fgets" in one place (reading Y/N) and not the other (reading the number). If you want the program to wait for input, then you need to make sure that all previous lines that were entered have been completely read in, including the new-line at the end of each line.

The "scanf" function is not very good for reading input because it does not know nor care about new-line characters or input lines. The new-line characters are the same as spaces as far as "scanf" is concerned.

These are all the same as far as "scanf" is concerned.

1\n
N\n
10\n
Y\n

1 N\n
10 Y\n

1 N 10 Y\n

1\n
N\n
10 Y\n

That wouldn't really matter much, except that the program is also displaying messages to prompt for the input. If the data that "scanf" wants to read has already been typed in before the prompt, then the program won't wait for any input after displaying the prompt message.

You could do something like this.

Code:

scanf( "%d ", &FAH );
That will skip white-space following the number, including a new-line. That still won't work if the input line looks like this.

1000N\n

When "scanf" tries to read white-space after the number, it will fail, since "N" is not white-space. That leaves the following in the input buffer.

N\n

And the next call to "scanf" or "fgets" will read the remainder of the line, just as if it had been typed in as a separate line.

The solution is to always use "fgets" to read entire input lines and then use "sscanf" to determine if the information that was entered is valid. That way you know that the program is not reading left-over information from some other input line.

lleb 05-19-2013 11:12 PM

so it would require an entire rework of the script to get what i want. ok.

just to make sure im following what you are saying. for my request for the FAH value at the start of the script i would have to do something like:

Code:

fgets( FAH, sizeof(FAH), stdin );
sscanf( FAH, "%d", <no_clue what goes here> );

is that at least on the right track?

Erik_FL 05-20-2013 12:28 PM

Quote:

Originally Posted by lleb (Post 4954623)
so it would require an entire rework of the script to get what i want. ok.

just to make sure im following what you are saying. for my request for the FAH value at the start of the script i would have to do something like:

Code:

fgets( FAH, sizeof(FAH), stdin );
sscanf( FAH, "%d", <no_clue what goes here> );

is that at least on the right track?

That's close. This is what I was suggesting.

Code:

fgets( BUFFER, sizeof(BUFFER), stdin );
sscanf( BUFFER, "%d", &FAH );

It reads an entire input line into "BUFFER" and then converts the characters to an integer value. The key point being that "fgets" will read the entire input line including the new-line.

lleb 05-20-2013 01:55 PM

progress:

Code:

#include<stdio.h>
#include<string.h>
#define TRUE  1
#define FALSE  0

//Problem Input
//int fahrenheit /* temperature in degrees Fahrenheit */
//Problem Output
//double celsius /* temperature in degrees Celsius */
//Relevant Formula celsius = 5/9 ( fahrenheit \u2212 32)

int main(void)

{
char ANS[2], Y, BUFFER[81];
do        {
       
        int FAH;
        double CEL;
       
        /* Get the degree in Fahrenheit */
        printf("\nPlease enter the temperature in Fahrenheit: ");
        //scanf("%d", &FAH);
        fgets( BUFFER, sizeof(BUFFER), stdin );
        sscanf( BUFFER, "%d", &FAH );

        /* Convert F to C */
        CEL = 5 * (FAH-32) / 9;

        /* Display results */
        printf("\n%d degrees Fahrenheit converts to %.2f degrees Celsius.\n", FAH, CEL);
        //sleep(10);
       
        /* Prompt for new input */
        printf("\nWould you like to enter a new temperature [Y]es or [N]o?  ");
        fgets( BUFFER, sizeof(BUFFER), stdin );
       
        }
        while ( sscanf( BUFFER, "%1s%*1s", ANS ) != 1 || strchr( "YyNn", *ANS ) == NULL  );
        return(0);

}

it not waits for a response, but always exits.

Code:

ssma-imac:ENG-3211 ssma$ gcc -o hw_1_5.v2 hw_1_5.v2.c
ssma-imac:ENG-3211 ssma$
ssma-imac:ENG-3211 ssma$
ssma-imac:ENG-3211 ssma$ ./hw_1_5.v2

Please enter the temperature in Fahrenheit: 75

75 degrees Fahrenheit converts to 23.00 degrees Celsius.

Would you like to enter a new temperature [Y]es or [N]o?  y
ssma-imac:ENG-3211 ssma$
ssma-imac:ENG-3211 ssma$
ssma-imac:ENG-3211 ssma$ ./hw_1_5.v2

Please enter the temperature in Fahrenheit: 75

75 degrees Fahrenheit converts to 23.00 degrees Celsius.

Would you like to enter a new temperature [Y]es or [N]o?  Y
ssma-imac:ENG-3211 ssma$
ssma-imac:ENG-3211 ssma$
ssma-imac:ENG-3211 ssma$ ./hw_1_5.v2

Please enter the temperature in Fahrenheit: 75

75 degrees Fahrenheit converts to 23.00 degrees Celsius.

Would you like to enter a new temperature [Y]es or [N]o?  N

do i need/want the #define TRUE/FALSE at the top of the script?

lleb 05-20-2013 02:01 PM

im reading up on the following links. they do explain a bit more about fgets and what not. still not sure what is wrong with the while statement preventing the script from restarting.

http://faq.cprogramming.com/cgi-bin/...&id=1043284385

http://faq.cprogramming.com/cgi-bin/...&id=1043284392

http://faq.cprogramming.com/cgi-bin/...&id=1043284392

i now see why you like fgets so much v scanf.

Erik_FL 05-20-2013 05:56 PM

Take a look at the last 10 lines. The "while" statement that you had at the end was checking for invalid input. To check for a yes answer you would need something similar to the "while" statement at the end.

Code:

int main(void)

{
char ANS[2], Y, BUFFER[81];
do        {
       
        int FAH;
        double CEL;
       
        /* Get the degree in Fahrenheit */
        printf("\nPlease enter the temperature in Fahrenheit: ");
        //scanf("%d", &FAH);
        fgets( BUFFER, sizeof(BUFFER), stdin );
        sscanf( BUFFER, "%d", &FAH );

        /* Convert F to C */
        CEL = 5 * (FAH-32) / 9;

        /* Display results */
        printf("\n%d degrees Fahrenheit converts to %.2f degrees Celsius.\n", FAH, CEL);
        //sleep(10);
       
        do  /* Do while not valid input */
        {
            /* Prompt for new input */
            printf("\nWould you like to enter a new temperature [Y]es or [N]o?  ");
            fgets( BUFFER, sizeof(BUFFER), stdin );
        }
        while ( sscanf( BUFFER, "%1s%*1s", ANS ) != 1 || strchr( "YyNn", *ANS ) == NULL  );
}
while ( *ANS == 'Y' || *ANS == 'y' );
return(0);
}


Beryllos 05-20-2013 06:10 PM

Quote:

Originally Posted by Erik_FL (Post 4954067)
[...]One can check to see if exactly the correct number of items was typed on each line.
Code:

do
{
    printf("\nWould you like to enter a new temperature [Y]es or [N]o?  ");
    fgets( BUFFER, sizeof(BUFFER), stdin );
}
while ( sscanf( BUFFER, "%1s%*1s", ANS ) != 1 || strchr( "YyNn", *ANS ) == NULL  );

Using "fgets" instead of "gets" limits the number of characters to the size of the character buffer. The "sscanf" function tries to read two character strings (tokens) of a single character. If just a "Y" or "N" is entered, then only one character string is returned and "sscanf" returns 1. If extra "garbage" is typed, then "sscanf" reads two strings and returns 2[...]

lleb, The reason your while-loop is not working as you expected is that you used this code from Erik_FL which is designed to loop back for invalid input (number of characters !=1 or character not in "YyNn"). You could make Erik_FL's while-loop the inside loop of 2 nested while-loops. Your loop would be the outer loop, with appropriate tests for the character value of ANS. Hint: you could use the return value of strchr( "YyNn",*ANS ).

(I see Erik_FL beat me to this answer. Well done.)

lleb 05-20-2013 10:01 PM

thanks guys. question about the {}'s. i notice they are not nested. it is like math were you only need to have the same number of { as you need } or is it not like in BASH were you need a { to open a section (function) and a } to close that same section.

just so that i can understand what the code is saying, let me see what i get.

sscanf ( BUFFER, "%ls%*ls", ANS )

that reads the BUFFER from above and puts in long string(?) to the variable ANS is that right?

!=1, not 100% on this, but reading beryllos this is anything other then YyNn? so kind of like the bash expression d != were the directory does not exist?

strchr( "YyNn", *ANS ) this takes the standard characters of YyNn and puts them into the string ANS?

not sure what the == NULL does, but im guessing its like 1>&2 in bash?

In the 2nd while statement (still not sure how the {} work to get that there with the do statement at the top of the script) this allows for any version of Yy to fill the string ANS. Kind of like BASH [Yy]?

please tell me what i have right and wrong. Thanks.

Beryllos 05-20-2013 11:47 PM

I can answer some questions, not all:
Quote:

Originally Posted by lleb (Post 4955383)
thanks guys. question about the {}'s. i notice they are not nested. it is like math were you only need to have the same number of { as you need } or is it not like in BASH were you need a { to open a section (function) and a } to close that same section.

The braces {} are nested and matched, they have to be, but opening and closing braces can be many lines apart. They mark the beginning and end of a function (for example, main{}) or a block of instructions (for example, the contents of a do{}while loop).

Code:

sscanf( BUFFER, "%1s%*1s", ANS ) != 1
Look for an explanation of this in an earlier post by Erik_FL. I don't know the syntax that well, but I think Erik said the sscanf function, written just so, counts how many characters were input by the user. If it is not equal to 1 (that's what !=1 means), the while-loop goes another cycle, repeating the user prompt and looking for user input.

Code:

||
That is a logical-OR operator.

Code:

strchr( "YyNn", *ANS ) == NULL
That strchr function returns a "valid pointer" (not sure if that is the correct terminology) if the character ANS occurs within the string "YyNn". For example, if ANS equals Y, I think it returns a pointer to the character Y in that string "YyNn". If ANS is a character not Y, y, N, or n (or the \0 which terminates the string), the function returns a NULL pointer. The == is a test for equality. In other words, if strchr("YyNn",*ANS) returns a valid pointer, the expression in the above code block becomes FALSE; if it returns a NULL pointer, the expression becomes TRUE.

Here are a few resources that may be helpful (not necessarily the best; just the first ones I found today that look good):

strchr function
http://www.codecogs.com/reference/co...g.h/strchr.php

relational operators (table including many programming languages)
http://en.wikipedia.org/wiki/Relatio...onal_operators

braces
Sorry, I can't find a clear introduction to the usage of braces in C. Perhaps an introductory textbook would help.

Erik_FL 05-21-2013 12:20 AM

Quote:

Originally Posted by lleb (Post 4955383)
thanks guys. question about the {}'s. i notice they are not nested. it is like math were you only need to have the same number of { as you need } or is it not like in BASH were you need a { to open a section (function) and a } to close that same section.

You need the same number of opening and closing braces. In C/C++ the indentation does not matter. You will see different styles for indentation. Here are some valid examples.

Code:

if ( i == 5 )
{
    printf( "The number is 5\n" );
}
else
{
    printf( "The number is %d\n", i );
}

if ( i == 5 )
    {
    printf( "The number is 5\n" );
    }
else
    {
    printf( "The number is %d\n", i );
    }

if ( i == 5 ) {
    printf( "The number is 5\n" );
}
else {
    printf( "The number is %d\n", i );
}

Braces in C are used to group statements into a "block" that is equivalent to a single statement. For example, "while" is a statement.

Code:

i = 5;
while ( i-- > 0 ) printf( "The count is %d\n", i );

To include multiple statements in the "while" statement one uses a block of statements.

Code:

while ( i-- > 0 )
{
    printf( "The count is %d\n", i );
    printf( "This is another statement in the while loop\n" );
}

Braces are also used to group the statements that make up the body of a function.

Quote:

Originally Posted by lleb (Post 4955383)
...just so that i can understand what the code is saying, let me see what i get.

sscanf ( BUFFER, "%1s%*1s", ANS )

that reads the BUFFER from above and puts in long string(?) to the variable ANS is that right?

The font is unfortunately not very good. Those are ones not Ls. It is scanning the contents of "BUFFER", a character string. The format specifier says to look for a one-character string (the Y or N) and store it in "ANS", then look for another one-character string and don't store it. If the information is correct, then "sscanf" should be able to find the first string, but NOT the second string. So, "scanf" should return a one to say that one item was found. If both items are found then there was more than a single non-blank character typed on the line. The "scanf" function will return 2. That also means "Yes" or "Yx" is not considered a valid answer, while "Y" is valid.

If you get rid of the "%*1s" then it just looks for the first non-blank character and does not care if there are more characters after that.

Code:

sscanf ( BUFFER, "%1s", ANS )
When you use "sscanf" or "scanf" the specifier "%1s" does not mean the same thing as "%c". Reading a single-character "string" with "%1s" skips blank characters and also stores a zero byte after the character (it stores two bytes). Reading a "character" with "%c" does not skip blanks and it stores just the 8-bit character (one byte). The only difference between an array of characters and a string is that a string has a zero byte to mark the end.

On a different subject, something that might not be obvious is these two expressions mean the same thing in C when "ANS" is a character array (string). Both expressions produce an 8-bit character value.

Code:

*ANS
ANS[0]

Also, these two expressions mean the same thing. Both expressions produce a character pointer (memory address of the array/string).

Code:

ANS
&ANS[0]

Quote:

Originally Posted by lleb (Post 4955383)
not 100% on this, but reading beryllos this is anything other then YyNn? so kind of like the bash expression d != were the directory does not exist?

strchr( "YyNn", *ANS ) this takes the standard characters of YyNn and puts them into the string ANS?
not sure what the == NULL does, but im guessing its like 1>&2 in bash?

The "strchr" function searches the string "YyNn" for the character in ANS[0]. If ANS[0] is one of those characters then "strchr" returns a pointer to the character that was found. If ANS[0] is NOT one of the characters then "strchr" returns a NULL pointer. So "== NULL" means that the character was NOT found.

C does not have I/O redirection statements like a scripting language. The default output always goes to a file called "stdout", for example, and "stdout" is usually set by the operating system or shell that runs the C program.

Quote:

Originally Posted by lleb (Post 4955383)
In the 2nd while statement (still not sure how the {} work to get that there with the do statement at the top of the script) this allows for any version of Yy to fill the string ANS. Kind of like BASH [Yy]?

It is checking to see if ANS[0] is the character 'Y' or 'y'. The "sscanf" statement found a one-character string (token), so ANS[0] has the first non-blank character that was typed on the input line.

The pairing of the braces is important. The entire statement is really like this.

Code:

do { /* statements... */ } while ( *ANS == 'Y' || *ANS == 'y' );
And with both loops it does something like this.

Code:

do { do { /* statements... */ } while ( /* not valid */ ); } while ( *ANS == 'Y' || *ANS == 'y' );
If you find it less confusing you could do something like this instead.

Code:

*ANS = 'y';
while ( *ANS == 'y' || *ANS == 'Y' )
{

    do {
          /* Statements */
    } while ( /* not valid */ );

}



All times are GMT -5. The time now is 06:31 PM.