Trouble getting a switch function to loop properly - c

I'm writing a program to 'encrypt' an inputted string of text by using a switch statement to correlate the given character with a symbol, and output that symbol in the place of the character. I put it in a while loop, the idea being that it would loop the full switch function each time until the received character is EOF. On a guess, I believe it is looping through just the first character, because I don't advance the getchar() statement, but I'm not sure how to do that so any help would be greatly appreciated. I say this because if I use return instead of break, it closes the while loop and only takes that first letter, if I use a break then it spams the first 'encrypted' char.
#include <stdlib.h>
#include <stdio.h>
/* C program to encrypt a given text message, assuming all lowercase */
int main() {
int Input, Encrypted;
printf("Please type your message\n");
Input = getchar();
while (Input != EOF) {
switch (Input) {
case 'a':printf("!"); break;
case 'b':printf("#"); break;
case 'c':printf("#"); break;
case 'd':printf("$"); break;
case 'e':printf("%"); break;
case 'f':printf("^"); break;
case 'g':printf("&"); break;
case 'h':printf("*"); break;
case 'i':printf("`"); break;
case 'j':printf("~"); break;
case 'k':printf("-"); break;
case 'l':printf("_"); break;
case 'm':printf("="); break;
case 'n':printf("+"); break;
case 'o':printf("["); break;
case 'p':printf("{"); break;
case 'q':printf("]"); break;
case 'r':printf("}"); break;
case 's':printf(";"); break;
case 't':printf(":"); break;
case 'u':printf("|"); break;
case 'v':printf(","); break;
case 'w':printf("<"); break;
case 'x':printf("."); break;
case 'y':printf(">"); break;
case 'z':printf("'");break;
return 0;
}
}
return 0;
}

The simplest solution would be to remove the line
Input = getchar();
and to replace the line
while (Input != EOF) {
with:
while ( (Input=getchar()) != EOF && Input != '\n' ) {
Alternatively, if you find this while condition too confusing, you could also use an infinite loop, instead, like this:
#include <stdlib.h>
#include <stdio.h>
int main( void )
{
printf("Please type your message\n");
for (;;) //infinite loop, equivalent to while(true)
{
int c;
c = getchar();
if ( c == EOF || c == '\n' )
break;
switch ( c )
{
case 'a':printf("!"); break;
case 'b':printf("#"); break;
case 'c':printf("#"); break;
case 'd':printf("$"); break;
case 'e':printf("%%"); break;
case 'f':printf("^"); break;
case 'g':printf("&"); break;
case 'h':printf("*"); break;
case 'i':printf("`"); break;
case 'j':printf("~"); break;
case 'k':printf("-"); break;
case 'l':printf("_"); break;
case 'm':printf("="); break;
case 'n':printf("+"); break;
case 'o':printf("["); break;
case 'p':printf("{"); break;
case 'q':printf("]"); break;
case 'r':printf("}"); break;
case 's':printf(";"); break;
case 't':printf(":"); break;
case 'u':printf("|"); break;
case 'v':printf(","); break;
case 'w':printf("<"); break;
case 'x':printf("."); break;
case 'y':printf(">"); break;
case 'z':printf("'"); break;
}
}
return 0;
}
Note that most character sets (such as ASCII) store the characters a to z consecutively. With these character sets, you don't need the long switch statement. Instead, you can simplify it to the following:
#include <stdlib.h>
#include <stdio.h>
int main( void )
{
printf("Please type your message\n");
for (;;) //infinite loop, equivalent to while(true)
{
const char map[] = "!##$%^&*`~-_=+[{]};:|,<.>'";
int c;
c = getchar();
if ( c == EOF || c == '\n' )
break;
if ( 'a' <= c && c <= 'z' )
putchar( map[c-'a'] );
}
return 0;
}

Related

Switch / Case issue

For C language, I know I'm close, but for for any character inputted, the printout is "Consonant". What is wrong with my switch case statement. Will I need if statements?
#include <stdio.h>
#include <stdlib.h>
int main()
{
char name;
printf("Enter a character: ");
scanf_s("%c", &name);
switch (name)
{
case 'a':
printf("Vowel");
break;
case 'e':
printf("Vowel");
break;
case 'i':
printf("Vowel");
break;
case 'o':
printf("Vowel");
break;
case'u':
printf("Vowel");
break;
case 'y':
printf("Sometimes");
break;
default:
printf("Consonant");
}
return 0;
}
Note that the Microsoft page for scanf_s() says:
In the case of characters, a single character may be read as follows:
char c;
scanf_s("%c", &c, 1);
Your scanf_s() call is failing, but you are not checking the result, so you don't know that.
ALWAYS check the result of input functions!
And make sure you've read the manual page for functions you're using.
It's probably also a good idea to use " %c" as the format string to skip white space. However, that's a refinement for later.
You don't have a break; after the code in the default: case label. That's not a good idea.
You can use:
if (scanf_s("%c", &name, 1) == 1)
{
switch (name)
{
case 'a':
case 'e':
case 'i':
case 'o':
case 'u':
printf("Vowel\n");
break;
case 'y':
printf("Sometimes\n");
break;
default:
printf("Consonant\n");
break;
}
}
else
fprintf(stderr, "Failed to read a character\n");
Note that all upper-case letters, punctuation, white space and control characters are deemed to be consonants. That's not entirely accurate.

Reading file line by line - why I get the first line only?

I have a task in university to write a C program which reads a file and counts the number of single and multi comments. The problem I have is that the second while() only reads the first line and so the returned comments are 0.
Previously I read the file character by character, but that's not the task requirement. Why does this program read only the first line and not the others?
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main(int argc, char **argv) {
FILE *fp;
int c, i = 0;
char path[256], ch, line[80];
unsigned int multi = 0;
unsigned int single = 0;
enum states {
PLAIN_TEXT,
SLASH,
STAR,
SINGLE_COMMENT,
MULTI_COMMENT,
QUOTES
} state = PLAIN_TEXT;
printf("Write file's name\n");
gets(path)
fp = fopen(path, "r");
if (!fp) {
// give an error message
} else {
while (fgets(line, sizeof(line), fp) != NULL) {
while (i < sizeof(line)) {
printf("%d.%c", i, line[i]);
switch (state) {
case PLAIN_TEXT:
switch (line[i]) {
case '/': i++;
state = SLASH;
break; // found a slash. In the next loop the switch argument will be SLASH
case '"': i++;
state = QUOTES;
break; // found a quote. Quoted text (there might be a '//' inside)
default: i++;
break; // found an ordinary character
}
break;
case QUOTES:
switch (line[i]) {
case '"': i++;
state = PLAIN_TEXT;
break; // Gets out the string;
case ' ':i++;
state = PLAIN_TEXT;
break;
default: i++;
state = QUOTES;
break; // Still a quoted text;
}
break;
case SLASH:
switch (line[i]) {
case '/': i++;
state = SINGLE_COMMENT;
break; // found a slash => a possible single comment found
case '*': i++;
state = MULTI_COMMENT;
break; // found a star => a possible multi comment found
default: i++;
state = PLAIN_TEXT;
break; // found an ordinary character
}
break;
case STAR:
switch (line[i]) {
case '/': i++;
state = PLAIN_TEXT;
multi++;
break; // Increments the multi comment and the next characher will be treated as a plain_taxt
default: i++;
state = MULTI_COMMENT;
break; // Still multi comment
}
break;
case SINGLE_COMMENT:
switch (line[i]) {
case '\n':i++;
state = PLAIN_TEXT;
single++;
break; // End of the single comment line. Increment the counter and the next character will be treated as a plain_text
default: i++;
break;
}
break;
case MULTI_COMMENT:
switch (line[i]) {
case '*': i++;
state = STAR;
break; // Found a multi comment. The next state will be star.
default: i++;
break;
}
break;
default: i++;
break;
}
}
}
fclose(fp);
printf("Single-comment : %8u\n", single);
printf("Multi-comment : %8u\n", multi);
}
return 0;
}
To enumerate the characters on the line, you must reinitialize i to 0 for each line and stop at the null terminator or at the newline character

How to store and return a string in C

My code (below) takes a text file with hexadecimal values and converts the hexadecimal value to binary and prints it. How can I modify this so that my hex_to_binary function can initialise a string array and add the converted binary to the array and then return the array. Then I just want to print the output of the hex_to_binary function by adding a printf statement in my main function. Thanks in advance
#include <stdio.h>
int hex_to_binary(char*);
int main(int argc, char *argv[])
{
FILE *file;
file = fopen(argv[1],"r");
char line[100];
while(!feof(file)){
fgets(line,100,file);
hex_to_binary(line);
}
fclose(file);
getchar();
return 0;
}
int hex_to_binary(char* hex_string)
{
int i=0;
printf("\nEquivalent binary value: ");
while(hex_string[i])
{
switch(hex_string[i])
{
case '0': printf("0000"); break;
case '1': printf("0001"); break;
case '2': printf("0010"); break;
case '3': printf("0011"); break;
case '4': printf("0100"); break;
case '5': printf("0101"); break;
case '6': printf("0110"); break;
case '7': printf("0111"); break;
case '8': printf("1000"); break;
case '9': printf("1001"); break;
case 'A': printf("1010"); break;
case 'B': printf("1011"); break;
case 'C': printf("1100"); break;
case 'D': printf("1101"); break;
case 'E': printf("1110"); break;
case 'F': printf("1111"); break;
case 'a': printf("1010"); break;
case 'b': printf("1011"); break;
case 'c': printf("1100"); break;
case 'd': printf("1101"); break;
case 'e': printf("1110"); break;
case 'f': printf("1111"); break;
}
i++;
}
return 0;
}
There are several ways to do this.
The one thing you can't do is declare a character array in the function, use sprintf to print to it, and return it. You cannot be certain that the space it points to exists anymore after the function returns.
That is, DON'T do this:
int hex_to_binary(char *hex_string)
{
char binary_string[MAX_SIZE + 1];
<...>
return binary_string;
}
To do exactly what you state, you can call malloc to obtain the space inside the function, then return a pointer to it:
int hex_to_binary(char *hex_string)
{
char *binary_string;
if((binary_string = malloc(MAX_SIZE + 1)) == NULL)
return -1; /* Indicate failure */
binary_string[0] = '\0';
<etc>
switch(hex_string[i])
{
case '0' : strcat(binary_string, "0000"); break;
<etc>
return binary_string;
}
This is permitted, because malloc is allocating the memory, probably in the heap, and this memory will still exist afterwards. The issue is, the function that calls hex_to_binary needs to call free() to release this memory, otherwise every call steals a little bit of memory -- this is known as a memory leak.
That is, do:
char *binary_line;
while(!feof(file)){
fgets(line,100,file);
binary_line = hex_to_binary(line);
printf("%s", binary_line);
free(binary_line);
}
NOT
while(!feof(file)){
fgets(line,100,file);
printf("%s", hex_to_binary(line););
}
If you were to call the hex_to_binary in a loop, once per hex digit, you could change the case statements to return a pointer to a string, instead of printing:
switch(hex_digit)
{
case '0' : return "0000";
case '2' : return "0001";
<etc>
This returns a pointer to constant string that you could print and saves freeing the space afterwards.
Another option would be to pass hex_to_binary two parameters, one with the string to convert, and one a pointer to the space to write it to:
int hex_to_binary(char *hex_string, char *binary_line)
{
<etc>
switch(hex_string[i])
{
case '0' : strcat(binary_string, "0000"); break;
<etc>
return 0;
}
Call it like this:
char binary_line[MAX_SPACE + 1];
while(!feof(file)){
fgets(line,100,file);
hex_to_binary(line, binary_line);
printf("%s", binary_line);
}
strcat() function is what you want.
if I were you, for simplicity I do this one below :
Define Global Char array :
char buffer[1000]; // define a space to write our string
....
Change your hex function like this :
while(hex_string[i])
{
switch(hex_string[i])
{
case '0': strcat(buffer,"0000"); break;
case '1': strcat(buffer,"0001"); break;
case '2': strcat(buffer,"0010"); break;
....
}
}
In main use this to print your string :
printf("%s\n",buffer);
Note: Don't forget to clear buffer after your each hex_to_binary function call (otherwise it keeps the previous numbers inside)
change to like this :
#include <stdio.h>
#include <string.h>
#include <ctype.h>
char *hexstr_to_binstr(char *outbuff, const char *s);
int main(int argc, char *argv[]){
FILE *file = fopen(argv[1],"r");
char line[100];
char binstr[4*sizeof(line)];
while(fgets(line, sizeof(line), file)){
puts(hexstr_to_binstr(binstr, line));
}
fclose(file);
getchar();
return 0;
}
const char *xdigit_to_binstr(char x){
switch(x){
case '0': return("0000");
case '1': return("0001");
case '2': return("0010");
case '3': return("0011");
case '4': return("0100");
case '5': return("0101");
case '6': return("0110");
case '7': return("0111");
case '8': return("1000");
case '9': return("1001");
case 'a':
case 'A': return("1010");
case 'b':
case 'B': return("1011");
case 'c':
case 'C': return("1100");
case 'd':
case 'D': return("1101");
case 'e':
case 'E': return("1110");
case 'f':
case 'F': return("1111");
default : return "";
}
return NULL;//never reach this point
}
char *hexstr_to_binstr(char *outbuff, const char *s){
char *p = outbuff;
for(; *s ; ++s){
if(isxdigit(*s)){
memcpy(p, xdigit_to_binstr(*s), 4);
p += 4;
}
}
*p = 0;
return outbuff;
}

Command Line Menu in C

I'm trying to implement a command line menu in C so that when the user enters a character, it will instantly process the character and carry out specific functions. The problem is, whenever I try to make it so that after each input is processed, the menu displays again and is ready for new input, the program will just continually read input and never process it unless I exit the program.
This is the code that works 1 time through:
char command;
command = getchar();
switch(command){
case 'c':
//create a new hash table;
break;
case 'l':
//look up a word;
break;
case 'f':
//read a file
break;
case 'p':
//print the table;
break;
case 'r':
//Remove a word
break;
case 'q':
exit(0);
break;
}
However, if I try to place it into an infinite loop to continually run, like I said, it will never process the inputs until I exit the program.
This code should work for you — it works for me. Note the use of int for the variable command.
#include <stdio.h>
#include <stdlib.h>
#include <ctype.h>
int main(void)
{
int command;
while ((command = getchar()) != EOF)
{
switch(command)
{
case 'c':
printf("Create a new hash table\n");
break;
case 'l':
printf("Look up a word\n");
break;
case 'f':
printf("Read a file\n");
break;
case 'p':
printf("Print the table\n");
break;
case 'r':
printf("Remove a word\n");
break;
case 'q':
printf("Quit\n");
exit(0);
break;
default:
printf("Unexpected input %d (0x%.2X) ('%c')\n",
command, command, isgraph(command) ? command : '.');
break;
}
}
return 0;
}

Case label does not reduce to an integer constant in C?

I am working on a game and I ran my code and got the error "case label does not reduce to an integer constant." I think I know what this means, but how do I fix it? Here is my code:
#include<stdio.h>
#include<stdlib.h
int player_cash[3] = {50};
char job[][20] {
'A',
'B',
'C',
"Donate",
"Go to work",
"Exit"
};
int jobs;
int main()
{
while(player_cash[0] > 0) {
printf("Please type A, B, C, Donate, Go to work, or Exit\n");
switch(jobs) {
case 'A':
player_cash[0]-=5;
player_cash[1]+=5;
printf("Cash=%i\n\n", player_cash[0]);
continue;
case 'B':
player_cash[0]-=5;
player_cash[2]+=5;
printf("Cash=%i\n\n", player_cash[0]);
continue;
case 'C':
player_cash[0]-=5;
player_cash[3]+=5;
printf("Cash=%i\n\n", player_cash[0]);
continue;
case "Donate":
player_cash[0]-=15; //Error here
player_cash[1]+=5;
player_cash[2]+=5;
player_cash[3]+=5;
printf("Cash donated\n\n");
printf("Cash=%i\n\n", player_cash[0]);
continue;
case "Go to work":
player_cash[0]+=10; //Error here
printf("Work done\n\n");
printf("Cash=%i\n\n", player_cash[0]);
continue;
case "Exit":
printf("Thanks for playing!\n\n"); //Error here
break;
default:
printf("Does not compute");
continue;
}
}
getchar();
return 0;
}
So, what I want the user to do is type in one of the options, and do the action that corresponds with it. How do I fix this?
You can't use strings as case expressions:
case "Donate":
Only integral expressions can be used, so e.g. case 'A': is OK.
Conceptually you have problems: jobs is an int, and you're testing for strings. If you want to allow the user to enter strings (more than a single character), you'll need to keep a string variable, and use something like fgets to get a full line of input.
Some of your case labels are characters (type char, indicated with 's). Those are integer constants.
Other labels are string literals (indicated with ") which have an effective type of const char *.1 Those are not integer constants and can not be used in this way.
1 For historical reasons they can often be used as if they were char *, but don't try to change them. Or else.
You cant compare string with c. "hello" == "hello" wont work as intend. switch only do simple c comparison on basic types.
switch(jobs) {
case 'A':
player_cash[0]-=5;
player_cash[1]+=5;
printf("Cash=%i\n\n", player_cash[0]);
continue;
case 'B':
player_cash[0]-=5;
player_cash[2]+=5;
printf("Cash=%i\n\n", player_cash[0]);
continue;
case 'C':
player_cash[0]-=5;
player_cash[3]+=5;
printf("Cash=%i\n\n", player_cash[0]);
continue;
case 'D':
player_cash[0]-=15; //Error here
player_cash[1]+=5;
player_cash[2]+=5;
player_cash[3]+=5;
printf("Cash donated\n\n");
printf("Cash=%i\n\n", player_cash[0]);
continue;
case 'G':
player_cash[0]+=10; //Error here
printf("Work done\n\n");
printf("Cash=%i\n\n", player_cash[0]);
continue;
case 'E':
printf("Thanks for playing!\n\n"); //Error here
break;
default:
printf("Does not compute");
continue;
}
as you only read a character in getch(), you can compare this value. (but ask the user to input only one letter because he input "Donate", getch() will first read D, return, then read o, etc..)
Your job array had inconsistent initializers (mixed char and const char *)
You can't use string literals as case labels, as the char pointers are not compile time constants. Use integers:
enum jobType
{
jobA,
jobB,
jobC,
jobDonate,
jobGoToWork,
jobExit,
/* marker */
jobInvalid
};
enum jobType typeOfJob(const char* const name)
{
int i;
for (i=jobA; i!=jobInvalid; ++i)
if (0 == strcmp(jobNames[i], name))
return i;
return i;
}
Also, the player_cash was 1 element short (and was written out of bounds at index [3])
Code sample also shows how to avoid general gets badness, do some basic line-end trimming and do case insenstive comparison (stricmp on windows, IIRC): http://liveworkspace.org/code/227015a4e51126d55ca4eb1eea739b02
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
int player_cash[4] = {50};
enum jobType
{
jobA,
jobB,
jobC,
jobDonate,
jobGoToWork,
jobExit,
/* marker */
jobInvalid
};
const char jobNames[][20] =
{
"A",
"B",
"C",
"Donate",
"Go to work",
"Exit"
};
enum jobType typeOfJob(const char* const name)
{
int i;
for (i=jobA; i!=jobInvalid; ++i)
#ifdef CASE_SENSITIVE
if (0 == strcmp(jobNames[i], name))
#else
if (0 == strcasecmp(jobNames[i], name))
#endif
return i;
return i;
}
const char* safer_gets()
{
static char input[1024];
char *p;
const char* t;
const char trimAt[] = "\r\n\t ";
fgets(input, sizeof(input), stdin);
for (t=trimAt; *t; ++t)
while(p = strrchr(input, *t))
*p = 0;
return input;
}
int main()
{
const char* input;
while(player_cash[0] > 0)
{
printf("Please type A, B, C, Donate, Go to work, or Exit\n");
input = safer_gets();
switch(typeOfJob(input))
{
case jobA:
player_cash[0]-=5;
player_cash[1]+=5;
printf("Cash=%i\n\n", player_cash[0]);
continue;
case jobB:
player_cash[0]-=5;
player_cash[2]+=5;
printf("Cash=%i\n\n", player_cash[0]);
continue;
case jobC:
player_cash[0]-=5;
player_cash[3]+=5;
printf("Cash=%i\n\n", player_cash[0]);
continue;
case jobDonate:
player_cash[0]-=15;
player_cash[1]+=5;
player_cash[2]+=5;
player_cash[3]+=5;
printf("Cash donated\n\n");
printf("Cash=%i\n\n", player_cash[0]);
continue;
case jobGoToWork:
player_cash[0]+=10;
printf("Work done\n\n");
printf("Cash=%i\n\n", player_cash[0]);
continue;
case jobExit:
printf("Thanks for playing!\n\n");
break;
default:
printf("Does not compute");
continue;
}
}
getchar();
return 0;
}

Resources