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/)

lleb 05-18-2013 06:18 PM

a C programming question; howto prompt for restart script?
 
I am in a C class, but we get a week off (only had 1 class and already a day off, Fri morning class) so I got to thinking about the three scripts the Prof. had us write for homework.

The scope is simple. create script that takes input, then prints the desired output to screen and end.

Id like to take it a bit further. Id like to figure out how to get the script to ask would you like to continue. if [Yy] then continue if anything else then exit.

In BASH id just create a simple if/then statement to call a function, but from reading around on google and in The ANSI C Programming Language book the examples they have for functions are the mathematics I am running in my main(void).

As i am still really a newB, i consider myself a newB at least, to BASH scripting I am not wrapping my head around the function(s) in C.

here is one of the programs id like to add the prompt to continue to:

Code:

/* 5. Write a program to convert a temperature in degrees Fahrenheit to degrees Celsius. */
//##########################################################
// Created by Raymond L. Brunkow
//
// This program is free software; you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 2 or version 3 of the
// license, at your option.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
//
//##########################################################
#include<stdio.h>

//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)

{

        int FAH;
        double CEL;

        /* Get the degree in Fahrenheit */
        printf("Please enter the temperature in Fahrenheit: ");
        scanf("%d", &FAH);

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

        /* Display results */
        printf("%d degrees Fahrenheit converts to %f degrees Celsius.\n", FAH, CEL);
        sleep(10);

        return(0);

}

sweet simple script. *note* the top line is the actual homework problem and as you can see i have met the requirements so I am not asking you to do my homework for me. That would not fly around here anyways :D...

What I'm asking is for examples of how I would turn the main(void) section into either a function, or how would I get the script to start back at the top to run again until the user input is != [Yy]???

Thanks.

lleb 05-18-2013 07:44 PM

*grumble*

ive been mucking around with do/while loops and just can not get it to work:

Code:

#include<stdio.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, Y;
do        {
        char ANS;
        int FAH;
        double CEL;
       
        /* Get the degree in Fahrenheit */
        printf("\nPlease enter the temperature in Fahrenheit: ");
        scanf("%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?  ");
        scanf("%c", &ANS);
        if (ANS == Y)
        {return(TRUE);}
        else
        {return(FALSE);}
        }
        while (1);
       
        return(0);

}

the program stops properly to get the F entry, it then properly displays the output, but never stops to wait for the Y/N for the last question, it just exits the program. the while (FALSE) and while (1) are not working and more importantly why is the program refusing to STOP and wait for an answer?

johnsfine 05-18-2013 08:48 PM

I think the first scanf leaves the newline in the input buffer. So the second scanf gets the newline the user typed for the first.

Anyway, the do and the while(1) are worthless because both sides of the if are return.

Beryllos 05-18-2013 09:17 PM

[QUOTE=lleb;4954023]
Code:

        scanf("%c", &ANS);
        if (ANS == Y)

I googled "c programming scanf character" and found that this problem is known. I have no first-hand experience with scanf, so I'll take their word for this: The previous scanf function leaves a newline character in the stdin buffer, which somehow feeds into the second scanf so it proceeds without user input. (Sounds a little weird to me, but let's continue...) Apparently it can be fixed by putting a space in the format specifier:
Code:

        scanf(" %c", &ANS);
The space, we are told, stands for any whitespace, including newlines.

By the way, I am not sure if your Y/N conditional is correctly written. I see that Y is declared as a character, but I don't see where it is assigned a value. I would write the conditional as:
Code:

        if (ANS == 'Y')
There is some difference between single and double quotes. You need single quotes in this context.

Erik_FL 05-18-2013 10:08 PM

The problem is that "scanf( )" does not read the new line "\n" at the end of a line. In fact, "scanf" is not guaranteed to read an entire line. It only reads what is necessary. For example, if you typed this in at the first prompt.

100y

Then BOTH of your "scanf( )" functions would succeed and return immediately. The first one reads "100" and stops because it sees a non-numeric character. the second one reads the "y" since there is another character that can be read.

Typing in a number followed by a new-line character reads only the number, and not the new-line after the number. So, "scanf("%d", &FAH)" leaves the new-line character waiting to be read. The next "scanf("%c", &ANS)" reads ANY character. It reads the new-line character of the first input line that you typed.

There are a few different ways around this problem. One way around the problem is to skip white-space in the "scanf" before reading the character.

Code:

scanf(" %c", &ANS);
White-space in the scanf specifier causes all white-space to be skipped up to the first non-white-space character.

Another way to solve the problem is to read a string instead of a character. Reading a string skips leading white-space. A string also ends at the first white-space. With "scanf" a string is really more like a token.

Code:

char ANS[2];
scanf( "%1s", ANS );

The best way to read input from the user is one line at a time. Then, use "sscanf" to parse each line.

Code:

char BUFFER[81];
char ANS[2];

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

This allows you to do a lot more error checking, because multiple items typed on the same line will not remain in the input buffer to be read by the next "scanf( )". Also, "sscanf( )" returns the number of items processed. 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. Note that using "*" in a "sscanf" specifier throws away the data instead of storing it. The program doesn't care what is the extra "garbage".

Reading numbers can work much the same way.

Code:

do
{
    printf("\nPlease enter the temperature in Fahrenheit: ");
    fgets( BUFFER, sizeof(BUFFER), stdin );
}
while ( sscanf( BUFFER, "%d%*1s", ANS ) != 1 );

You may be trying to write a program to read a Y or N response without requiring a new-line. Reading input without waiting for a new-line is difficult. Some input devices won't allow your program to "read" any input until a new-line is typed. That's because lines can be edited before they are entered. Even using "getchar( )" may not return any character until a new-line is entered. So, reading individual key-presses requires special functions that are dependent on the operating system.

lleb 05-18-2013 11:23 PM

Erik FL, you are way over my head, but i think i understand some of what you are saying. I attempted to incorporate the code you have for the Y/N response, but I am getting the following error when compiling:

Code:

ssma-imac:ENG-3211 ssma$ gcc -o hw_1_5.v2 hw_1_5.v2.c
hw_1_5.v2.c: In function ‘main’:
hw_1_5.v2.c:51: warning: passing argument 1 of ‘fgets’ makes pointer from integer without a cast
hw_1_5.v2.c:54: warning: incompatible implicit declaration of built-in function ‘strchr’

here is the modified script:

Code:

#include<stdio.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        {
        char ANS, BUFFER;
        int FAH;
        double CEL;
       
        /* Get the degree in Fahrenheit */
        printf("\nPlease enter the temperature in Fahrenheit: ");
        scanf("%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);

}

thanks all for helping me understand more and get ahead in class.

Erik_FL 05-19-2013 04:11 PM

Quote:

Originally Posted by lleb (Post 4954085)
I am getting the following error when compiling:

Code:

ssma-imac:ENG-3211 ssma$ gcc -o hw_1_5.v2 hw_1_5.v2.c
hw_1_5.v2.c: In function ‘main’:
hw_1_5.v2.c:51: warning: passing argument 1 of ‘fgets’ makes pointer from integer without a cast
hw_1_5.v2.c:54: warning: incompatible implicit declaration of built-in function ‘strchr’


The error on line 51 is because you need to use character strings (character arrays) rather than single character variables.

Code:

char ANS[2], BUFFER[81];
What the above does is define "ANS" as an array of two characters. For example, "ANS[0]" and ANS[1]". To refer to the whole array, you can use "ANS" and it is the same as a pointer to a character "&ANS[0]". When you read the 1 character string into ANS then ANS[0] will have the 'Y' or 'N' and ANS[1] will have the zero terminator '\0'.

The input buffer "BUFFER" is 81 characters, so the first 80 characters can be text, leaving the last character for the zero terminator. Of course if the user enters less text, the zero terminator will be somewhere else. For example typing in "test" will result in BUFFER[0] through BUFFER[3] being "test" and BUFFER[4] will be 0.

The error on line 54 is because you need to include a header file to define the "strchr" function.

Code:

#include <string.h>
The "string.h" header file defines the "strchr" function. The "strchr" function returns a "char *". If you don't include the header file, the default (implicit) definition of the "strchr" function is to return an integer. Since "strchr" is also a built-in function the compiler warns you that you are trying to redefine an already defined built-in function.

The other thing you may be wondering about is the "sizeof( )" function. It is actually a compiler built-in function. It returns the size of a variable in bytes. For a character array, the size in bytes is always equal to the size in characters. The zero terminator takes up a byte, so an array of 81 characters can hold a character string that has 80 characters. If you use "sizeof( )" for other kinds of data, keep in mind that they may be more than one byte each. For example, sizeof(int) is usually 4 and sizeof(short) is usually 2.

Code:

int numbers[3];

printf( "The array is %d bytes and can hold %d numbers\n", sizeof(numbers), sizeof(numbers)/sizeof(int) );

The above prints a message saying that the array is 12 bytes and can hold 3 numbers.

In cases where the type of the data in the array might be changed later, you can divide by the total size of one array element.

Code:

printf( "The array is %d bytes and can hold %d numbers\n", sizeof(numbers), sizeof(numbers)/sizeof(numbers[0]) );
The above will work no matter what type of data is in the array. It could be an array of double-precision floating point numbers that are 8 bytes each.

One of the hardest concepts to understand in C is the difference between a character, a character array, and a string. A character is a single byte (8-bits). A character array is a sequence of characters, with a fixed size. A string is a variable-length sequence of characters ending with a 0. The zero at the end of a string is a normal character, and takes up a byte. You can store a string in a character array if the array is large enough for the characters and the terminating 0 byte. There may be other characters after the 0 byte if the array is larger than the string.

Adding to the confusion, C does not have a separate "string" data type. A "char *" could be a pointer to a single character, a pointer to all or part of a character array, or a pointer to a string. That is determined by how the program and functions use the pointer.

A string constant is really just a convenient way of declaring a constant array of characters, and then using the pointer to that array.

Code:

const char msg[] = { 'T', 'e', 's', 't', '\n', 0 };
const char *msg2 = "Test\n";

puts(msg);
puts(msg2);
puts("Test\n");
puts("Test\n");

Although "puts" outputs the same text for each call, the character pointer (memory address) is usually different each time. Some really smart C compilers may actually figure out that all four (or maybe three) of the strings are identical constants. In that case, some of the pointers (memory addresses) might be exactly the same. Also note that "msg2" does not contain a string! Since "msg2" is a pointer to a string, it contains the address of the string constant that is on the same line. If the program were to assign a different pointer, then "msg2" could refer to a totally different string.

Code:

msg2 = "New text string";
puts(msg2);

So, what happened to the text string ""Test\n"" that was on the same line where "msg2" was originally defined? That text string may still exist somewhere in memory, but the program is no longer able to obtain the address of that string. For all practical purposes, the string can no longer be used in the program. It's really no different than using a string constant as a parameter in a function call. Smarter C compilers will remember if a string constant exists in memory and use the exact same constant where it appears again.

Also, "msg2" is not a constant, the characters that "msg2" points to are constant. Otherwise, how could the program change the pointer? The following would make msg2 a constant pointer to constant characters.

Code:

const char *const msg2 = "Test\n";
Now it is not possible to do this.

Code:

msg2 = "New text string";
Some people find it easier to understand if it is written a different way.

Code:

const char *(const msg2) = "Test\n";
const char* const msg2 = "Test\n";
const char* (const msg2) = "Test\n";
(const char*) (const msg2) = "Test\n";

Why does any of that matter? Strictly speaking, you can do anything to a non-constant variable than you can to a constant variable. The advantage is for the compiler to warn you if you do something unintended. By saying that something is a constant, you are telling the compiler that you don't ever intend to change it. Perhaps you used "msg2" in a lot of places and just defined it to avoid having lots of copies of the exact same string constant. In that case you really don't want to allow the program to accidentally change "msg2".

lleb 05-19-2013 05:44 PM

ok. much information.

i added the new include, but i am still getting an error with the fget line. reading around there seem to be 2 standards. the one you provided above and one that has a -1 for the 2nd part...

Code:

fgets( BUFFER, sizeof(BUFFER), stdin );
and
Code:

fgets( BUFFER, sizeof(BUFFER)-1, stdin );
neither seem to make much of a difference in the error im getting...

Code:

$ gcc -o hw_1_5.v2 hw_1_5.v2.c
hw_1_5.v2.c: In function ‘main’:
hw_1_5.v2.c:52: warning: passing argument 1 of ‘fgets’ makes pointer from integer without a cast

from what little i can follow in your above post, not you just me being so new to C, the stdin is passing as an integer even though we have BUFFER defined as a character array. that is confusing. if it is defined as a character array(or string im sure im using the wrong term here), then why does the compiler think its an integer?

Erik_FL 05-19-2013 06:29 PM

Quote:

Originally Posted by lleb (Post 4954495)
ok. much information.

i added the new include, but i am still getting an error with the fget line. reading around there seem to be 2 standards. the one you provided above and one that has a -1 for the 2nd part...

Make sure that you have "BUFFER" declared as an array of characters.

Code:

char BUFFER[81];
That should eliminate the error. Note the error message refers to parameter 1 of "fgets" and that is "BUFFER".

Using the "-1" isn't necessary since "fgets" already subtracts one from the length.

chrism01 05-19-2013 06:39 PM

Read the definition of fgets() http://linux.die.net/man/3/fgets.
1st arg must be ptr-to-char, not char.

lleb 05-19-2013 07:27 PM

thanks found the compiling error issue. i was redefining both ANS and BUFFER as char instead of strings. i deleted that line and it was then able to compile. Now i need to figure out how to get the script to wait for user input before it starts over. now it just prints the question, does not wait, then restarts the script. so it is at least restarting, just want it to wait for the Y/N response to it will exit properly too.

Erik_FL 05-19-2013 08:14 PM

You need to make sure that the first new-line character (following the number) is read in before the program reads the Y/N answer. One way to do that is to use "fgets" to read the entire input line.

Code:

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

Another way to handle the problem is to read and discard the characters up to the first new-line.

Code:

scanf( "%d", &FAH );
while ( getchar( ) != '\n' );


lleb 05-19-2013 08:19 PM

Quote:

Originally Posted by Erik_FL (Post 4954547)
You need to make sure that the first new-line character (following the number) is read in before the program reads the Y/N answer. One way to do that is to use "fgets" to read the entire input line.

Code:

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


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]?
Quote:


Another way to handle the problem is to read and discard the characters up to the first new-line.

Code:

scanf( "%d", &FAH );
while ( getchar( ) != '\n' );



chrism01 05-19-2013 08:39 PM

In C, a single 'char' fits exactly into the space for an int, therefore you can (under certain circumstances) treat them either way, depending on context.
See the ASCII table http://www.asciitable.com/.

Its been while since I've written any C, but as an example you can check for eg a lowercase single alpha char using arithmetic methods; something like (off the top of my head)
Code:

if ( x >= 'a' && x <= 'z' )
{
    /* x is lowercase char */
.
.
}

UNTESTED :)

I used to like this book a lot when i was learning C http://www.amazon.com/Book-Programmi.../dp/0201183994 (A Book on C; Auth by Kelley & Pohl).
Extremely clear teaching, inc line by line analysis of examples.
I highly recommend you lookup each new C (library) fn you come across and become familiar with its args, & rtn value (if any).
Details, Details.... a very powerful lang, but takes some focus to pick up.
:)

lleb 05-19-2013 09:29 PM

thanks.

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 07:18 AM.