Function that reads the file doesn't work. Interactive menu problem - c

Im having some problems making my newly started project to work (also im a beginner).
For some reason option number 4 in my interactive menu doesn't work and just takes a default route (doesn't output whats inside a file (file directory is fine.).
At this point i've read every forum searching for answer but couldn't modify my code in any way that would work.
So I decided to ask you for help.
Here is my code:
#include <stdio.h>
#include <stdlib.h>
#define kFileLocation "/Users/patrykpiwowarczyk/Desktop/STUDIA/FoCP/Kodowanie/TestProjektSemestralnyAngielski/TestProjektSemestralnyAngielski/authors.txt"
void options();
void start(void);
void score(void);
void optionz(void);
void author(void);
void pexit(void);
int main(void)
{
char ch;
int num;
char option;
while (1) {
printf("****Your English Learning Index Cards****\n\n");
printf("Enter 1-5 of the following options: \n\n");
options();
scanf("%c", &option);
switch (option) {
case '1':
break;
case '2':
break;
case '3':
break;
case '4':
author();
break;
case '5':
pexit();
break;
default:
printf("Please insert number ranging from 1-5 though... No cheating! \n\n");
printf("Press ENTER key to Continue\n");
}
}
return 0;
}
void options()
{
printf("1. Start Game \n");
printf("2. View Scoreboard \n");
printf("3. Options \n");
printf("4. Author \n");
printf("5. Exit \n\n");
}
void author()
{
char c;
FILE *authorsFile;
if ((authorsFile = fopen("/Users/patrykpiwowarczyk/Desktop/STUDIA/FoCP/Kodowanie/TestProjektSemestralnyAngielski/TestProjektSemestralnyAngielski/authors.txt","r")) == NULL)
{
printf("FAILED to read the file, maybe check directory?\n");
exit(1);
}
while ((c = fgetc(authorsFile)) != EOF)
{
printf("%c", c);
}
fclose(authorsFile);
}
void pexit()
{
puts("Your progress has been saved, see you next time.");
exit(0);
}
if you could help me in any way I would appreciate it soo much..
Greetings, Patryk Piwowarczyk.
PS: the #define kFileLocation is a leftover from my other tries. Omit it.

Based on your comments, I conclude the following:
The problem was that scanf correctly wrote the digit into the variable option the first time it was called. However, the second time scanf was called, it immediately returned the newline character from the previous menu selection, instead of waiting for the user to enter another digit. Whenever scanf returned a newline, the default case was triggered.
Therefore, the problem can be best solved by changing the scanf call to the following:
scanf(" %c", &option);
By adding a space to the start of the format string, you instruct scanf to discard all whitespace characters before reading the character. That way, you can insure that a newline will never be written into the option variable.
The problem of scanf reading newline characters instead of discarding them has been discussed in more detail in this question.

Related

C language - Menu issue with multiple void statements and do-switch-case

I am trying to make a menu with the options to compress a text inputted by the user and then store that value to be extracted in the extract menu option.
The issue lies in that it seems like the code isn't following the void statements, for example
case 1: compress();//compress statement
It seems to only get the printf statement in the void compress(void) and not the scanf, which it then follows with the loop of the menu.
Any solutions?
#include <stdio.h>
#include <string.h>
#include <time.h>
#include <stdlib.h>
void menu(void);
void compress(void);
void extract(void);
int main(void)
{
menu();
return 0;
}
void menu(void)
{
int choice;
do
{
printf("Menu\n\n");
printf("1. Compress Text\n");
printf("2. Extract Text\n");
printf("3. Exit\n");
scanf_s("%d", &choice);
switch (choice)
{
case 1: compress();//compress statement
break;
case 2: extract();//extract statement
break;
case 3: printf("Ciao\n");
exit(0);
break;
default: printf("Invalid Entry. Try again\n");
break;
}
} while (choice != 3);
}
void compress(void) {
printf("\n-------------------------\n");
printf(" COMPRESS ");
printf("\n-------------------------\n");
printf("\nPlease enter a word/sentence to be compressed:\n");
char txt[200];
scanf_s("%c", &txt);
printf("\nYour word/sentence is %c", txt, "\n");
char comp = strlen(txt);
int mask[200]{};
for (int i = 0; i < comp; ++i) //loop until all leters are masked
{
mask[i] = i + 127;
printf("\nYour compressed word/sentence is %c ", mask[i]);
}
return;
}
void extract(void) {
printf("\n-------------------------\n");
printf(" EXTRACT ");
printf("\n-------------------------\n");
return;
}
You are scanning only one single character, and as "%c" doesn't skip white-space this is the newline character terminating the previous input.
You instead want to read in a string, and to be on the safe side you should add the maximum length to read to: "%199s" (note: one less than array size to leave space for the terminating null character):
scanf_s("%199s", txt);
Note, too, that as txt is an array it decays to a pointer automatically when being passed to a function (see above); taking the address of (&txt) produces a pointer with the same value, but of a different type: char(*)[200]. This pointer is not compatible to neither %c nor %s format specifier, thus you actually produce *undefined behaviour!
Note, too, that scanf_s (any function from scanf family) will stop reading at the first whitespace – a sentence might, though contain multiple words separated by whitespace. You'd just read the first one of them, though. So you might want to drop scanf for this input entirely in favour of e.g. fgets:
fgets(txt, sizeof(txt), stdin);
Note, here, too, that the previous scanf("%d", ...) did not consume the terminating newline, so you'll need to ignore that, e.g. by a preceding call to getchar.
Crediting this last point to Jonathan Leffler who hinted to in his comment to the question)

Prompt error when user enters a char instead of an expected int

I'm making a menu that lists options 1-3. The user is expected to enter an integer.
scanf("%d", &select_option)
How do I prompt error when user enters a char (for example "a", or "asd" for long strings, or a mixture like "1a2") instead of an expected int? Thanks.
Note: When the user enters a 'char' like 'a', 'asd', the code goes into an infinite loop for some reason.
Here's my program (minimal example):
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main(void)
{
printf("Favourite sports? \n");
printf("1. Tennis\n");
printf("2. Badminton\n");
printf("3. Basketball\n");
printf("4. Exit program.\n");
printf("Enter your choice (1-4): ");
scanf("%d", &select_option);
while(select_option != 4)
{
switch(select_option)
{
case 1:
printf("You like tennis! Nice! \n");
break;
case 2:
printf("You like badminton! Nice!");
break;
case 3:
printf("You like basketball! Nice!");
break;
default:
system("clear");
printf("Invalid option. Please re-enter your choice (1-4).\n");
}//end switch
printf("Favourite sports? \n");
printf("1. Tennis\n");
printf("2. Badminton\n");
printf("3. Basketball\n");
printf("4. Exit program.\n");
printf("Enter your choice (1-4): ");
scanf("%d", &select_option);
}//end while
}//end main
You could do this:
#include <stdio.h>
int main(void) {
int v;
int ret = scanf("%d", &v);
if(ret == 1)
printf("OK, %d\n", v);
else
printf("Something went wrong!\n");
return 0;
}
where I took advantage of the return value of scanf(), and based on that value, I made an assumption. This will fail for the case of "1a2", but will succeed for "12" and "a".
However, this is a broad question and personally the way I would go for it is:
Use fgets() to read input.
Discard newline.
Convert string to integer (with strtol() for example).
Validate input.
I am assuming u are a beginner. You can use Switch Case which is used usually for creating menus and depending on the choice of the user executes the particular case.
I will show u a small example.
#include<stdio.h>
#include<conio.h>
int main()
{
int n;
printf("Select the sports u want to do\n");
printf("1.Tennis\n2.Karate\n3.Football\n");
scanf("%d",&n);
Switch(n)
{
case 1:printf("You chose Tennis\n");
break; //To prevent from all cases being executed we use
//break which helps from coming out of a loop
case 2:printf("You chose Karate\n");
break;
case 3:printf("You chose Football\n");
break;
default:printf("Please enter an appropriate number !");
//Cases which dont match with the input are handled by default !
}
}
Also to make the user enter input until he wants to exit add a while loop with a variable !
I hope this helps!

Code giving me an infinite loop

#include <stdio.h>
void load_menu(void);
int main(void)
{
load_menu();
return 0;
}
void load_menu(void)
{
int choice;
int loopagain;
do
{
printf("Menu \n\n");
printf("Please enter your choice: \n");
printf("1. \n");
printf("2.\n");
printf("3.\n");
printf("4. Exit\n");
if (scanf("%d",&choice)==1)
{
switch(choice)
{
case 1:
break;
case 2:
break;
case 3:
break;
case 4: printf("Quitting program!\n");
break;
default: printf("Invalid choice! Please try again\n");
printf("\n");
break;
}
}
else
{
printf("Characters are invalid, please enter a number: \n ");
if (scanf("%d",&loopagain)==1)
load_menu();
}
}while((choice !=4));
}
why is this still giving me an infinite loop when I enter a character? It is a menu (the case statements still need to be filled) but i am taking care of the character input by the if statement but it still does not seem to work. Thanks
If the character input is invalid, the loopagain in the newly-called load_menu() won’t be the same as in its caller. Don’t recurse at all:
else
{
printf("Characters are invalid, please enter a number: \n ");
choice = 0; // Unused, so continue the loop
}
I believe, aside from the problems identified so far, is that the offending "letter" is stuck in the input buffer. When reading a number with scanf, it stops as soon as it hits anything not whitespade and not a digit. So if the buffer contains "a\n", and we call scanf("%d", ...), then scanf will return immediatelty, and will keep on doing so until the offening 'a' has been removed from the buffer.
What we need is a little loop to remove the offending "rubbish" from the input buffer.
Here's that question asked before (although the flushing is for a slightly different reason, the solution is the same):
Question about flushing buffer
After you enter something the is not a digit, and as such is not accepted by scanf("%d",&choice) the input buffer is not flushed. I believe you should be able to fix this issues with a call to fflush(stdin) when handling unacceptable inputs. Better, you would probably be better servde to flush the input buffer after each time you call scanf.
To my mind, your handling of an incorrect input doesn't make sense. It should be handled more like your default: case, I think. The recursive call, as has been stated by others, doesn't make sense, nor does calling scanf again for input when you are about to go back to displaying the menu and getting user input yet again.
I think your problem is the loopagain variable. By the name, you were thinking about this variable like a flag to loop or not again and to manage the way your second loop would go. Since you are reading it from stdin(scanf) you'll lost the control over it.
Since you already have one scanf in your implementation and since it is a loop, you won't need recursive calls and you can use always the same scanf, using the loopagain variable/flag in the proper way.
Even better is that in this way, there's no char wich its integer value is 4(it woul never pass the scanf test with value 1, but still...) besides the EOT (ascii - cntr-D) wich is a non-common one, and you can think of it as an alternative way to break your program.
One soluiton is this(I guess, by my interpretation) :
#include <stdio.h>
void load_menu(void);
int main(void)
{
load_menu();
return 0;
}
void load_menu(void)
{
int choice;
int loopagain = 1;
do
{
if(loopagain != 0){ /*You'll set it to different from 0 if the user entered a 'bad' number so the menu is only printed once*/
printf("Menu \n\n");
printf("Please enter your choice: \n");
printf("1. \n");
printf("2.\n");
printf("3.\n");
printf("4. Exit\n");
}
if (scanf("%d",&choice)==1)
{
switch(choice)
{
case 1:
break;
case 2:
break;
case 3:
break;
case 4: printf("Quitting program!\n");
break;
default:printf("Invalid choice! Please try again\n");
loopagain = 0;
printf("\n");
break;
}
}else{
printf("Characters are invalid, please enter a number: \n ");
loopagain = 0;
}
}while(choice !=4);
}
Hope it helped.

how to create an interactive menu in C that moves from one function into another without redrawing the menu

My goal is to produce a program that will take a file as input and "encode" the text within by shifting the characters ahead 3 (so 'a' would be come 'd'). It should produce an output file with the encoded text. The menu is to take user input and execute the function that is assigned to the number selected.
I'm early on at creating this program, but running short on time and am struggling with how to structure it. Currently, I have the menu displaying, but when a sub function is called, it displays but then the menu overwrites it and I can't figure out why. Any help would be appreciated. Here is the code I have so far...
#include <stdio.h>
#define INPUT_FILE 1 //define statements
#define OUTPUT_FILE 2
#define NUM_TO_SHIFT 3
#define ENCODE 4
#define QUIT 0
int menu(); //function prototypes
int input();
int output();
int shift();
int encode();
void quit();
int main()
{
int choice; // main variables
char user_filename[100];
choice = menu(); // get user's first selection
while(choice != QUIT) //execute so long as choice is not equal to QUIT
{
switch(choice)
{
case INPUT_FILE:
printf("Enter the filename of the file to encode:\n");
printf("(hit the Enter key when done)\n");
gets(user_filename);
break;
case OUTPUT_FILE: output();
break;
case NUM_TO_SHIFT: shift();
break;
case ENCODE: encode();
break;
case QUIT: quit();
break;
default: printf("Oops! An invalid choice slipped through. ");
printf("Please try again.\n");
}
choice = menu(); /* get user's subsequent selections */
}
printf("Bye bye!\n");
return 0;
}
int menu(void)
{
int option;
printf("Text Encoder Service\n\n");
printf("1.\tEnter name of input file (currently 'Secret.txt')\n");
printf("2.\tEnter name of output file (currently not set)\n");
printf("3.\tEnter number of characters data should be shifted (currently +7)\n");
printf("4.\tEncode the text\n\n");
printf("0.\tQuit\n\n");
printf("Make your selection: ");
while( (scanf(" %d", &option) != 1) /* non-numeric input */
|| (option < 0) /* number too small */
|| (option > 4)) /* number too large */
{
fflush(stdin); /* clear bad data from buffer */
printf("That selection isn't valid. Please try again.\n\n");
printf("Your choice? ");
}
return option;
}
int input()
{
}
int output()
{
return 2;
}
int shift()
{
return 3;
}
int encode()
{
return 4;
}
void quit()
{
printf("Quiting...Bye!");
exit(0);
}
You shouldn't use gets(user_filename) to get the file name since gets() reads up to a \n and stops reading. Your scanf for the menu option does not read the \n at the end of the line when the user types in the menu option. Essentially, you're making gets read a string without words in it. The line you want to read is actually the next line. Using scanf instead of gets will fix it.
Otherwise, your program is working as expected - it's just that your functions don't do anything yet that your menu is "overwriting" the submenus. See http://ideone.com/F2pEs for an implementation with scanf instead of gets.
use getchar(); soon after the gets(user_filename); it will wait to get the character
gets(user_filename);
getchar();
break;
As in this question which Stackoverflow has highlighted as a match, you need to clear out the buffer to remove the newline that's waiting in there.
Add this code after reading a valid menu option:
do
{
c = getchar();
} while (c != EOF && c != '\n');
where c is a char declared up by option. This loops over remaining characters in the input stream until EOF (End Of File) or a newline character is reached, meaning they don't affect your call to gets(). Note that gets() is considered insecure because it doesn't protect against buffer overflow, a user could easily enter more than 100 characters (inc. newline) and start writing into memory that shouldn't be touched by their input. You would do well to lookup the secure equivalent when you see compiler warnings around function calls like this, typically they take a second parameter which is the maximum size of the buffer being read into.
Well, this answer is way late but having come across it, I can't help but write something.
Let's get straight to it. You will have an array of menus, with the array elements being the options you want in your menu. Then while in a truthy condition, loop through the elements of the array, selecting the option you want.
#include "stdio.h"
#include "stdlib.h"
#include "string.h"
//function prototypes
int input();
int output();
int shift();
int encode();
void quit();
int main(){
int menus_on = 1;
const char *menus[5] = {"Input","Output","Shift","Encode","Quit"};
while(menus_on){
int menu,*temp;
for(int i =0;i<6;i++){
printf("%d: %s\n",i,menus[i]);
}
printf("Select menu\n");
scanf("%d",temp);
menu = *temp;
printf("Selected menu::%d\n",menu);
switch(menu){
case 0:
input();
break;
case 1:
output();
break;
case 2:
shift();
break;
case 3:
encode();
break;
case 4:
quit();
break;
default:
printf("Invalid selection\n");
break;
}
}
return 0;
}
int input() {
return 0;
}
int encode () {
return 0;
}

gets not taking the password

In the code below in lesson2() i have used a password to enter the function but when i enter the function it does not takes in the passord and says incorrect password.By not taking in the password,i mean to say that i have used gets but its waiting for me to input a password.please dont tell me not to use gets!
#include<stdio.h>
#include<conio.h>
#include<string.h>
#include<dos.h>
int mistakes=0,mistakes_length,len;
char temp[100];
void lesson1(void);
void lesson2(void);
void result(void);
void main(void)
{
int choice;
clrscr();
printf("Enter a lesson number to practise:1-10 \n");
scanf("%d",&choice);
switch(choice)
{
case 1:
lesson1();
result();
break;
case 2:
lesson2();
result();
break;
default:
printf("You did not entered a valid choice program quitting..\n");
exit(0);
}
getch();
}
void lesson1(void)
{
int i;
char str1[100]="testing the typing tutor if it works";
mistakes_length=5;
clrscr();
gotoxy(25,2);
puts("Welcome to lesson 1");
puts(str1);
len=strlen(str1);
for(i=0;i<len;i++)
{
temp[i]=getche();
if(strncmpi(&str1[i],&temp[i],1))//does not match
{
mistakes++;
sound(100);
delay(1000);
nosound();
}
}
getch();
}
void result(void)
{
printf("Your statsistics are as under:\nYou performed this lesson with %d mistakes\n",mistakes);
if(mistakes>=mistakes_length)
{
printf("\n Your typing is very bad");//allow anything to be typed with any mistake in lesson 1
}
if(mistakes>3&&mistakes<5)
{
printf("Bad!,You need to practise this excercise more\n");
}
if(mistakes>=1&&mistakes<=3)
{
printf("Good!you can still do better\n");
}
if(mistakes==0)
{
printf("Excellent!You are qualified for the next lesson\n");
printf("The next lessons password is \n\t\t\t:12345");
}
}
void lesson2(void)
{
char password[]="12345",str2[]="My name is khan and i am not a criminal";
int i;
mistakes=0,mistakes_length=0,
printf("Enter password:\n");
gets(temp);
if(strcmp(temp,password))
{
gotoxy(20,25);
printf("Wrong Password,Program Quitting.\n");
getch();
exit(1);
}
gotoxy(25,25);
printf("Password Accpted!");
getch();
clrscr();
gotoxy(25,2);
printf("Welcome to lesson 2\n");
printf("Type the text shown below:\n");
puts(str2);
len=strlen(str2);
for(i=0;i<len;i++)
{
temp[i]=getche();
if(strncmp(&str2[i],&temp[i],1));
{
mistakes++;
}
}
getch();
}
I think that it is the scanf and gets together making a problem!
Your problem is that prior to calling gets(), you have called scanf("%d",&choice); (in main()). The problem with this is that console input is line oriented. This means that although you are only waiting for a number to be entered the user has to enter a complete line ending in newline. The %d format specifier only consumes the digit characters leaving the rest of the line in the buffer to be used by the next console input call; which in this case is gets() which sees the newline in the buffer and returns an empty string without waiting for further input.
A solution:
scanf("%d",&choice);
while(getchar() != '\n' ) { /*no nothing*/}
I think the problem you are seeing is in fact coming from the main method.
The scanf() call that you make is only looking for "%d" rather than "%d\n".
Therefore, you enter the number 2 in order to try test 2, and must press enter before that value gets to the program. Since scanf is not trying to match it, that newline remains as part of the input stream and thus is passed directly to gets() within the next function.
Therefore, if you change the string within scanf inside the main function, you should see the code start working.
Now on another note (and I know that you asked us not to but...) you really shouldn't use gets(). In order to switch to fgets, just replace gets(temp) with fgets(temp,99,stdin). That 99 is any number that is less than the size of the temp buffer you have made, which in this case has size 100.
Try printing temp like this: printf("[%s]\n", temp); after the gets(temp); to see what is saved in temp. It may takes as input a previous given input from lesson1 function?
If this is the case, something like this maybe will fix the problem:
char other_tmp[5];
gets(other_tmp);
gets(temp);
although in such a case you should better correct the lesson1 function instead.
it supposed to work.
edit your code:
int r = gets(temp);
printf("returned: %d, Entered: %s\n", r, temp);
and post here the result

Resources