I wrote a very simple Hangman game that requests user input then makes the user guess up to six times.
The first letter of the word is always shown and if the user inputs the same letter that has already been uncovered it is not seen as a mistake,
I want to improve my code to make it accept lowercase input if the hangman string consists of Uppercase letters.
For example if the input string is "HELLO" and the user sees this H_____ on the console and he inputs lowecase 'e' then the letter should be uncovered and printed as a capital letter as the input string.
So after the user inputs e the output should look like this HE___
However with the way I implemented it just converts all uppercase Letters to lowercase Letters.
How can I make the code accept lowercase userinput and print out Uppercase user input if a Uppercase letter is in the string?
Thanks for any help
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <ctype.h>
int main(void)
{
char hangmanWord[100], tempWord[100];
char hangmanOutput[100];
int wrongTry = 6 , matchFound = 0;
int tries = 0;
int counter = 0 , position = 0, winner, length , i;
char alphabetFromUser;
scanf("%s",hangmanWord);
getchar();
length = strlen(hangmanWord);
for( i = 1; i < length ; i++)
{
hangmanOutput[i] = '_';
hangmanOutput[length] = '\0';
}
for(i = 0 ; i < length ; i++)
{
if(i==0){
printf("%c",hangmanWord[0]);
}
printf("%c",hangmanOutput[i]); //line output
if(i==length-1){
printf("(%d)",tries);
}
}
while(wrongTry != 0) /**while loop for exiting the program when no try left**/
{
matchFound = 0;
putchar('\n');
printf("letter: ");
scanf("%c",&alphabetFromUser);
fflush(stdin);
if (matchFound != 2)
{
for(counter=0;counter<length;counter++) /**for loop to check whether player input alphabet exists or not in the word**/
{
if(hangmanWord[counter]>= 'A' && hangmanWord[counter] <= 'Z'){
hangmanWord[counter] = tolower(hangmanWord[counter]);
}
if(alphabetFromUser==hangmanWord[counter])
{
matchFound = 1;
}//end of if()
}//end of
if(matchFound == 0) /**in case of wrong guess**/
{
wrongTry--;
tries++;
}//en
else
{
for(counter = 0; counter < length; counter++)
{
matchFound = 0;
if(alphabetFromUser == hangmanWord[counter])
{
position = counter ;
matchFound = 1;
}//end of if
if(matchFound == 1)
{
for(i = 0 ; i < length ; i++)
{
if( i == position)
{
hangmanOutput[i] = alphabetFromUser; /**Put the alphabet at right position**/
}
else if( hangmanOutput[i] >= 'a' && hangmanOutput[i] <= 'z' ) /** If the position already occupied
by same alphabet then no need to
fill again EASY!! and continue */
{
continue;
}
else
{
hangmanOutput[i] = '_'; /** Put a underscore at not guessed alphabet position **/
}
}
tempWord[position] = alphabetFromUser; /**put the alphabet in another char array to check with the original word**/
tempWord[length] = '\0';
/**put the NULL character at the end of the temp string**/
winner = strcmp(tempWord+1,hangmanWord+1); /**upon True comparison it will return 0**/
if(winner == 0) /**if the player guessed the whole word right then he/she is the WINNER**/
{
for(i = 0 ; i < length ; i++)
{
if(i==0){
printf("%c",hangmanWord[0]);
}
printf("%c",hangmanOutput[i+1]); //line output
if(i==length-1){
printf("(%d)",tries);
}
}
getchar();
return 0;
}//end of inner if
}//end of outer if
}//end of for loop
}//end of else
} // end of if(matchFound != 2) condition
for(i = 0 ; i < length ; i++)
{
if(i==0){
printf("%c",hangmanWord[0]);
}
printf("%c",hangmanOutput[i+1]); //line output
if(i==length-1){
printf("(%d)",tries);
}
}
getchar();
}
if(wrongTry <= 0) /**if the player can not guess the whole word in 5 chaces**/
{
putchar('\n');
printf("DEAD\n");
return 0;
}
getchar();
return 0;
}
Looking at ascii table, the lower case letters from a-z are from 0x6_, to 0x7_ and the upper case ones from 0x4_ to 0x5_, which means that all we have to do is to toggle off the second bit of the second nibble in order to convert a lowercase letter to an uppercase letter.
(a) a-z are as follows 0110 xxxx, 0111 xxxx
(b) A-Z are as follows 0100 xxxx, 0101 xxxx
Let's assume that we have the character 'a', which is 0110 xxxx. If we use the AND bitwise with 0x5F => 0110 xxxx & 0101 xxxx => 0100 xxxx => 'A'
char upper(char ch) {
return(ch & 0x5F);
}
Now that we have the function, we can use it. Looking at your code, you are checking the hangman array twice in case the user has typed in a lower case letter or a uppercase letter so instead of doing that we could do that once
int32_t right_letter;
int32_t tries;
int32_t win;
right_letter = 0;
/* since one letter is already discovered */
win = length - 1;
while(wrongTry != 0) {
/* read from user */
for(counter = 0; counter < length; ++counter) {
if(hangmanOutput[counter] == '_'){
if(upper(alphabetFromUser) == upper(hangmanWord[counter])) {
hangmanOutput[counter] = hangmanWord[counter];
right_letter = 1;
++win;
}
}
if(win + 1 == length)
/* win */
wrongTry -= !right_letter;
right_letter = 0;
}
I haven't tested the code, but it should work. Try to understand what I have changed and why.
EDIT: #Andrew Henle had mentioned in the comments that the OP hadn't specified the character set, therefore I assumed that It's ASCII.
For an ASCII only input, it is no brainer....Just exploit the fact that char type in C can in fact be used as int and do the maths.
#include <stdio.h>
#include <stdlib.h>
int main(){
char ch;
printf("letter : ");
ch = getc(stdin);
if (ch >= 'a' && ch <= 'z')
ch -=32;
else if (ch >= 'A' && ch <= 'Z')
ch += 32;
printf("output : %c\n", ch);
return 0;
}
Another version is given below. You can replace lower_case and upper_case with any other alphabet you want and boom, it will work.
#include <stdio.h>
#include <string.h>
char convert(char ch){
const char *lower_case = "abcdefghijklmnopqrstuvwxyz";
const char *upper_case = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
char *p;
if ((p = strchr(lower_case, ch)) != NULL)
return *(upper_case+(p-lower_case));
else if ((p = strchr(upper_case, ch)) != NULL)
return *(lower_case+(p-upper_case));
else
return ch;
}
int main(){
char ch;
printf("letter : ");
ch = getc(stdin);
printf("output : %c\n", convert(ch));
return 0;
}
Related
My code works to delete any vowels and prints the first letter of the word as a capital letter.
How can I get my expected output to work?
If the value is " I am Iron Man" (with a leading space), it works and prints "M Rn Mn".
However, without the space at the beginning of the string, my output is "m Rn Mn" but
the expected output is "M Rn Mn".
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main() {
char str[] = "I am Iron Man";
int i, j, len = 0;
len = strlen(str);
// Accepting input.
for (i = 0; i < len; i++) {
// Check vowels.
if (str[i] == 'a' || str[i] == 'e' || str[i] == 'i' || str[i] == 'o' || str[i] == 'u' ||
str[i] == 'A' || str[i] == 'E' || str[i] == 'I' || str[i] == 'O' || str[i] == 'U') {
// delete vowel syntax
for (j = i; j < len; j++) {
// Store after removing vowels
str[j] = str[j + 1];
}
i--;
len--;
}
str[len + 1] = '\0';
}
for(i=0; str[i]!='\0'; i++)
{
//check first character is lowercase alphabet
if(i==0)
{
if((str[i]>='a' && str[i]<='z'))
str[i]=str[i]-32; //subtract 32 to make it capital
continue; //continue to the loop
}
if(str[i]==' ')//check space
{
//if space is found, check next character
++i;
//check letter if lowercase
if(str[i]>='a' && str[i]<='z')
{
str[i]=str[i]-32; //subtract 32 to make it capital
continue; //continue to the loop
}
}
}
printf("%s", str);
return 0;
}
Your problem lies with excessive use of the continue statement in the second for loop. The second continue is just plain pointless, as control reaches the end of the loop, anyway, after the point where you have that.
But the first continue is actually causing the fault: after removal of the vowels, the first character in the modified string will be a space – so, the first if block inside the second loop will be entered, and that will skip the check for a lowercase letter following the space.
Removing those continue statement will fix your code.
Also, note that you can use the islower and toupper functiosn to check for lowercase letters and convert to uppercase:
#include <stdio.h>
#include <string.h>
#include <ctype.h> // For islower and toupper
int main()
{
char str[] = "I am Iron Man";
size_t i, j, len = 0;
len = strlen(str);
// Accepting input.
for (i = 0; i < len; i++)
{
// Check vowels.
if (str[i] == 'a' || str[i] == 'e' || str[i] == 'i' || str[i] == 'o' || str[i] == 'u' ||
str[i] == 'A' || str[i] == 'E' || str[i] == 'I' || str[i] == 'O' || str[i] == 'U') {
// delete vowel syntax
for (j = i; j < len; j++)
{
// Store after removing vowels
str[j] = str[j + 1];
}
i--;
len--;
}
str[len + 1] = '\0';
}
for (i = 0; str[i] != '\0'; i++)
{
//check first character is lowercase alphabet
if (i == 0)
{
if (islower(str[i])) {
str[i] = toupper(str[i]);
}
// A "continue" here is wrong ... it will skip the following check for a lowercase letter
}
if (str[i] == ' ') //check space
{
//if space is found, check next character
++i;
//check letter if lowercase
if (islower(str[i]))
{
str[i] = toupper(str[i]);
// No need for a "continue" here ... we're already at the end of the loop
}
}
}
printf("%s\n", str);
return 0;
}
I have another solution for you, that may be a bit easier to comprehend:
#include <stdio.h>
#include <string.h>
#include <ctype.h>
int main(void) {
char str[] = "I am Iron Man";
char *in;
char *out;
int up = 1; // very simple state, if "up" then next character should be made upper
for (in = str, out = str; *in; in++) {
if (strchr("aeiouAEIOU", *in) != NULL) {
// do nothing
} else if (*in == ' ') {
*out++ = *in;
up = 1; // we see a space, so next letter should be upper
} else if (up) {
*out++ = toupper(*in);
up = 0; // we see a letter (or other character), ignore case
} else {
*out++ = *in;
}
}
*out = '\0';
printf("%s\n", str);
}
Or, if you don't like/understand the pointer syntax:
#include <stdio.h>
#include <string.h>
#include <ctype.h>
int main(void) {
char str[] = "I am Iron Man";
int i;
int o;
int up = 1; // very simple state, if "up" then next character should be made upper
for (i = 0, o = 0; str[i]; i++) {
if (strchr("aeiouAEIOU", str[i]) != NULL) {
// do nothing
} else if (str[i] == ' ') {
str[o++] = str[i];
up = 1; // we see a space, so next letter should be upper
} else if (up) {
str[o++] = toupper(str[i]);
up = 0; // we see a letter (or other character), ignore case
} else {
str[o++] = str[i];
}
}
str[o] = '\0';
printf("%s\n", str);
}
In both cases, a very simple state is used. For more complex conditions, you should learn about state machines. In this case, the up state indicates that the next letter should be capitalised.
Note that if you want to remove leading spaces, after "removing" the vowels, you need to modify the logic a bit:
#include <stdio.h>
#include <string.h>
#include <ctype.h>
int main(void) {
char str[] = "I am Iron Man";
char *in = str; // we initialize in and out here already
char *out = str;
int up = 1; // very simple state, if "up" then next chacter should be made upper
// we skip leading vowels AND spaces, this is a special case
while (*in && (strchr("aeiouAEIOU ", *in) != NULL)) {
in++;
}
// now we are at the first character that is not a vowel or space
for ( ; *in; in++) {
if (strchr("aeiouAEIOU", *in) != NULL) {
// do nothing
} else if (*in == ' ') {
*out++ = *in;
up = 1; // we see a space, so next letter should be upper
} else if (up) {
*out++ = toupper(*in);
up = 0; // we see a letter (or other character), ignore case
} else {
*out++ = *in;
}
}
*out = '\0';
printf("%s\n", str);
}
Well now you have a few examples to study that take a bit of a different approach. See if you understand the logic, and try to make it so that other characters like e.g. ( and ) also delimit words.
One of the problems is that you've got too much code. It iterates through the entire array once to strip out vowels, then again to adjust the case of the first letter of each word. Imagine this is processing data that is measured in Gb. A second pass is unnecessary.
(And, there are standard library functions like isalpha() and toupper() that you should use. Don't write code with "magic numbers".)
It's worth studying a program's 'flow control', without resorting to arbitrary 'continue' statements to affect that flow.
It's also worth starting from scratch with a minimal block of code in main(), then developing your algorithm in a function (or several). Avoid the tendency to have one long, linear program all inside main(). If you can put functionality into 'compartments', each can be developed and tested and forgotten about as the program grows more complex.
#include <stdio.h>
#include <string.h>
#include <ctype.h>
// A single pass "compacts" the data (no vowels) while also using some single operations
//tracking changing from one word to the next (first letter to uppercase.)
char *func( char *str ) {
for ( int d = 0, s = 0, up = 0; (str[d] = str[s]) != '\0'; s++)
if( !strchr( " aeiouAEIOU" + !!up, str[d] ) ) {
if( str[d] == ' ' )
up = 1;
else if( up++ < 2 )
up++, str[d] = (char)toupper( (unsigned char)str[d] );
d++; // 'd'estination idx only increments here!
}
return str;
}
int main(void) {
// sample test strings
char *strs[] = {
"I am Iron Man",
" I am Iron Man ",
"Iron Man am I",
" Iron Man am I",
"The man of steel",
" The man of steel",
};
for( size_t i = 0; i < sizeof strs/sizeof strs[0]; i++ )
puts( func( strs[i] ) );
return 0;
}
M Rn Mn
M Rn Mn
Rn Mn M
Rn Mn M
Th Mn F Stl
Th Mn F Stl
I'm writing code which checks if an array is palindrome or not:
Write a program that reads a message, then checks whether it's a palindrome
(the letters in the message are the same from left to right as from right to left):
Enter a message: He lived as a devil, eh?
Palindrome
Enter a message: Madam, I am Adam.
Not a palindrome
When I have entered He lived as a devil, eh?,
it gives me the output Not a palindrome,
but the real output should be palindrome.
Below code is what I have tried so far.
#include <stdio.h>
#include <ctype.h>
#define MAX_LEN 100
int main(void) {
char message[MAX_LEN];
char c, *p = message, *q;
printf("Enter a message: ");
while ((c = toupper(getchar())) != '\n' & p < message + MAX_LEN) {
if (isalpha(c))
*p++ = c;
}
p--;
for (q = message; q < p; q++, p--) {
if (*p != *q) {
printf("Not a palindrome\n");
return 0;
}
}
printf("Palindrome\n");
return 0;
}
For starters you should declare the variable c as having the type int. The user can interrupt the input process in which case the function getchar returns integer value EOF and you should check whether this occurred.
char *p = message, *q;
int c;
There is a bug in the condition of the while statement
while ((c = toupper(getchar())) != '\n' & p < message + MAX_LEN) {
Instead of the bitwise operator & you have to use the logical AND operator &&.
As I already have said you should check in the condition of the while statement whether the user interrupted the input. For example
while ( p < message + MAX_LEN && ( c = toupper(getchar())) != EOF && c != '\n') {
if (isalpha(c))
*p++ = c;
}
The argument of a call of toupper or isalpha should be converted to the type unsigned char. Otherwise in general without the casting such a call can invoke undefined behavior.
It is desirable not to exclude from an entered string numbers. SO it is better at least to call the function isalnum instead of the function isalpha.
The user can enter an empty string in this case this decrement of the pointer
p--;
also can invoke undefined behavior.
And it is better when a program has one point to exit.
The program can look the following way
#include <stdio.h>
#include <ctype.h>
#define MAX_LEN 100
int main(void)
{
char message[MAX_LEN];
printf( "Enter a message: " );
char *p = message;
for ( int c; p < message + MAX_LEN && ( c = getchar() ) != EOF && c != '\n'; )
{
if( isalnum( ( unsigned char )c ) )
{
*p++ = toupper( ( unsigned char )c );
}
}
int palindrome = 1;
if ( p != message )
{
for ( char *q = message; palindrome && q < --p; ++q )
{
palindrome = *q == *p;
}
}
printf( "The entered message is %spalindrome\n",
palindrome ? "" : "not " );
return 0;
}
Its output might look for example like
Enter a message: He lived as a devil, eh?
The entered message is palindrome
or like
Enter a message: Madam, I am Adam
The entered message is not palindrome
Pay attention to that instead of using a loop with numerous calls of the function getchar you could use only one call of the function fgets
fgets( message, sizeof( message ), stdin );
or
if ( fgets( message, sizeof( message ), stdin ) != NULL )
{
// check whether the entered string is a palindrome
}
Before check the palindrome you have to remove white spaces and punctuation marks. For an example if you use civic?, it is not palindrome because of ?. In the other hand if you use civ ic, it is not palindrome because of white space. There for
Convert all letters to uppercase or lowercase.
Remove white spaces.
remove punctuation marks.
Check palindrome or not.
You can do it by using # include <string.h>
First thing is you have to use scanf() which accept string with white space.
printf("Enter a string = ");
scanf("%[^\n]%*c", word);
Then you have to convert that string to Uppercase or Lowercase because a != A. We know civic is a palindrome but Civic is not a palindrome('Civic != civiC) because Uppercase letters have different ASCII values and Lowercase letters have different ASCII values.
(a - z) -: 97 - 122
(A - Z) -: 65 - 90
In my case I have converted lowercase to uppercase.
while(strlen(word) >= i)
{
if(word[i] >= 97 && word[i] <= 122)
{
word[i] = word[i] - 32;
}
i++;
}
Another case is your if you enter civ ic with white space, it's palindrome word is ci vic. You can see civ ic != ci vic. There for you have to remove white spaces in your program. And also you have to remove punctuation marks because if you use civic, it's reversed word is ,civic'. You can seecivic, != ,civic`.
int len = strlen(word);
while(a < len)
{
for(i = 0; i < len; i++)
{
if(word[i] == ' ' || !(word[i] >= 'A' && word[i] <= 'Z'))
{
for(j = i; j < len; j++)
{
word[j] = word[j+1];
}
len--;
}
}
a++;
}
Final thing is we have to revers our string and need to check if our reversed string is equal to our original string. If it is true our String is palindrome. If it is false our String is not a palindrome.
for(i = 0; i < len; i++)
{
if(word[i] == word[len - 1])
{
len--;
}
else
{
printf("%s is not a palindrome\n", word);
return 0;
}
}
printf("%s is a palindroeme\n", word);
This the full code after you merging above parts
# include <stdio.h>
# include <string.h>
int main (void)
{
char word[100];
int i = 0;
int j, x = 0;
int a = 0;
printf("Enter a string = ");
scanf("%[^\n]%*c", word);
while(strlen(word) >= i)
{
if(word[i] >= 97 && word[i] <= 122)
{
word[i] = word[i] - 32;
}
i++;
}
printf("After converting it to uppercase = %s\n", word);
int len = strlen(word);
while(a < len)
{
for(i = 0; i < len; i++)
{
if(word[i] == ' ' || !(word[i] >= 'A' && word[i] <= 'Z'))
{
for(j = i; j < len; j++)
{
word[j] = word[j+1];
}
len--;
}
}
a++;
}
printf("After removing spaces = %s\n", word);
for(i = 0; i < len; i++)
{
if(word[i] == word[len - 1])
{
len--;
}
else
{
printf("%s is not a palindrome\n", word);
return 0;
}
}
printf("%s is a palindroeme\n", word);
return 0;
}
First test Output -:
Enter a string = He lived as a devil, eh?
After converting it to uppercase = HE LIVED AS A DEVIL, EH?
After removing spaces = HELIVEDASADEVILEH
HELIVEDASADEVILEH is a palindroeme
Second test Output -:
Enter a string = Madam I am Adam.
After converting it to uppercase = MADAM I AM ADAM.
After removing spaces = MADAMIAMADAM
MADAMIAMADAM is not a palindrome
Hi I am confined to stdio.h, stdlib.h and string.h and I need to ask a user for input - the input can be any number of characters between 1 and 6, however the first two characters MUST be an uppercase alphabetical letter, and the remaining four characters MUST be a number between 0 and 9.
Examples of valid input:
AB1
AB1234
AB
A
Examples of Invalid Input:
AB12345 (too many characters)
123 (first two characters are not uppercase letters)
ABA (a character after the second one is not a numeric value)
Here is my attempt so far (just bear in mind I have almost no experience with C, the likelihood that this solution is "idiomatic" is next to none, and the reason I am asking this is so that I can learn):
Flightcode is a char array defined as flightcode[7] it lives inside another struct called flight. I am fgetsing it into a temp_array[7] first and then strcpying it into the flight->flightcode such that the null terminator is appended and I don't know a better way of doing that.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define MAX_FLIGHTCODE_LEN 6
#define MAX_CITYCODE_LEN 3
#define MAX_NUM_FLIGHTS 50
#define DB_NAME "database"
typedef struct {
int month;
int day;
int hour;
int minute;
} date_time_t;
typedef struct {
char flightcode[MAX_FLIGHTCODE_LEN + 1];
date_time_t departure_dt;
char arrival_city[MAX_CITYCODE_LEN + 1];
date_time_t arrival_dt;
} flight_t;
date_time_t departure_dt;
date_time_t arrival_dt;
char * scanline(char *dest, int dest_len);
int main(){
char temp_string[100];
flight_t flight[MAX_NUM_FLIGHTS + 1];
int correct_code = 0;
printf("Enter flight code>\n");
scanline(temp_string, sizeof(flight->flightcode));
strcpy(flight->flightcode, temp_string);
while(correct_code == 0)
{
for(int i = 0; flight->flightcode[i] != '\0' && correct_code == 0; i++)
{
while((i < 2 && (flight->flightcode[i] <= 64 || flight->flightcode[i] >= 91)) || (i > 1 && (flight->flightcode[i] < 48 || flight->flightcode[i] >= 58)))
{
printf("Invalid input.\n");
scanline(temp_string, sizeof(flight->flightcode));
strcpy(flight->flightcode, temp_string);
}
if((i < 2 && (flight->flightcode[i] > 64 || flight->flightcode[i] < 91)) || (i > 1 && (flight->flightcode[i] >= 48 || flight->flightcode[i] < 58)))
{
correct_code = 1;
}
}
}
}
char * scanline(char *dest, int dest_len){
int i, ch;
i = 0;
for (ch = getchar();
ch != '\n' && ch != EOF && i < dest_len -1; ch = getchar())
dest[i++] = ch;
dest[i] = '\0';
while (ch != '\n' && ch != EOF)
ch = getchar();
return (dest);
}
Scansets and the %n specifier could be used to parse the input.
The format string "%n%2[A-Z]%n%4[0-9]%n" uses the %n specifier in three places to capture the number of characters processed. The scanset %2[A-Z] will scan up to two characters if the characters are in the set of upper case letters. %4[0-9] will scan up to four characters if the characters are digits.
If two values are scanned by sscanf, the number of characters processed are subtracted to make sure there are two leading upper case characters and six or fewer total character and the trailing character is the terminating zero.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define MAX_FLIGHTCODE_LEN 6
#define MAX_CITYCODE_LEN 3
#define MAX_NUM_FLIGHTS 50
#define DB_NAME "database"
typedef struct {
int month;
int day;
int hour;
int minute;
} date_time_t;
typedef struct {
char flightcode[MAX_FLIGHTCODE_LEN + 1];
date_time_t departure_dt;
char arrival_city[MAX_CITYCODE_LEN + 1];
date_time_t arrival_dt;
} flight_t;
date_time_t departure_dt;
date_time_t arrival_dt;
char * scanline(char *dest, int dest_len);
int main(){
int head = 0, leading = 0, tail = 0;
int correct_code = 0;
int result = 0;
char temp_string[100];
char upper[3] = "";
char digits[5] = "";
flight_t flight[MAX_NUM_FLIGHTS + 1];
do {
printf("Enter flight code>\n");
scanline(temp_string, sizeof(temp_string));
if ( 0 < ( result = sscanf ( temp_string, "%n%2[A-Z]%n%4[0-9]%n", &head, upper, &leading, digits, &tail))) {
if ( 1 == result && 0 == temp_string[leading]) {
correct_code = 1;
break;
}
if ( 2 == result && 2 == leading - head && 7 > tail - head && 0 == temp_string[tail]) {
correct_code = 1;
}
else {
printf ( "invalid input\n");
}
}
else {
printf ( "invalid input\n");
}
} while(correct_code == 0);
printf ( "Input is: %s\n", temp_string);
strcpy(flight->flightcode, temp_string);
return 0;
}
char * scanline(char *dest, int dest_len){
int i, ch;
i = 0;
for (ch = getchar(); ch != '\n' && ch != EOF && i < dest_len -1; ch = getchar()) {
dest[i++] = ch;
}
dest[i] = '\0';
while (ch != '\n' && ch != EOF) {
ch = getchar();
}
return dest;
}
Your function scanline does not do much more than the standard function fgets. I propose to use the standard function instead. Removing the trailing newline '\n' is easy.
I have split the checks into 3 parts:
Check the length to be more than 0 and not more than MAX_FLIGHTCODE_LEN.
Check the first 2 characters to be uppercase letters A..Z
Check the remaining characters to be digits 0..9
Proposed code:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define MAX_FLIGHTCODE_LEN 6
#define MAX_CITYCODE_LEN 3
#define MAX_NUM_FLIGHTS 50
#define DB_NAME "database"
typedef struct {
int month;
int day;
int hour;
int minute;
} date_time_t;
typedef struct {
char flightcode[MAX_FLIGHTCODE_LEN + 1];
date_time_t departure_dt;
char arrival_city[MAX_CITYCODE_LEN + 1];
date_time_t arrival_dt;
} flight_t;
date_time_t departure_dt;
date_time_t arrival_dt;
int main(void){
char temp_string[100];
flight_t flight[MAX_NUM_FLIGHTS + 1];
int correct_code;
size_t len;
int i;
do
{
/* we first assume the code is correct and set this to 0 on any error */
correct_code = 1;
printf("Enter flight code>\n");
if(fgets(temp_string, sizeof(temp_string), stdin) == NULL)
{
if(feof(stdin)) fprintf(stderr, "no input (EOF)\n");
else perror("fgets");
correct_code = 0;
temp_string[0] = '\0';
}
if(correct_code)
{
len = strlen(temp_string);
/* cut off newline
* Use a loop to handle CR and LF just in case Windows might leave more than one character */
while((len > 0) &&
((temp_string[len - 1] == '\n') ||
(temp_string[len - 1] == '\r')))
{
len--;
temp_string[len] == '\0';
}
if(len > MAX_FLIGHTCODE_LEN)
{
correct_code = 0;
fprintf(stderr, "Input must not be longer than %d characters.\n", MAX_FLIGHTCODE_LEN);
}
if(len == 0)
{
correct_code = 0;
fprintf(stderr, "Empty input.\n");
}
}
/* check first two letters */
for(i = 0; (i < 2) && (i < len) && correct_code; i++)
{
/* you could use function isupper when you make sure the locale is set to "C" */
if((temp_string[i] < 'A') || (temp_string[i] > 'Z'))
{
correct_code = 0;
fprintf(stderr, "first two characters must be uppercase letters. Found '%c' at position %d\n", temp_string[i], i);
}
}
/* check digits starting from 3rd character */
for(i = 2; (i < MAX_FLIGHTCODE_LEN) && (i < len) && correct_code; i++)
{
/* you could use function isdigit here */
if((temp_string[i] < '0') || (temp_string[i] > '9'))
{
correct_code = 0;
fprintf(stderr, "Third to last characters must be digits. Found '%c' at position %d\n", temp_string[i], i);
}
}
if(correct_code)
{
/* we already checked that length is not more than MAX_FLIGHTCODE_LEN, so we don't need strncpy to avoid buffer overflow */
strcpy(flight->flightcode, temp_string);
printf("Valid code: %s\n", flight->flightcode);
}
else
{
fprintf(stderr, "Invalid code.\n");
}
} while(!correct_code);
return 0;
}
You have a requirement that does not fit well with what scanf can easily do, so I would stay away from it, and use fgets as a primary read utility.
But as the number of acceptable uppercase and digit characters is not fixed by only limited I would use a custom parser based on a state machine. It is probably not the most elegant nor efficient way but it is simple, robust and easy to maintain.
Just to demonstrate it, I have allowed blank characters before the first uppercase one and spaces after the last digit. So the following code accept an arbitrary long line following this regex pattern [ \t]*[A-Z]{1,maxupper}[0-9]{0,maxdigit}\s* provided it receives a buffer of size at least maxupper+maxupper+1. It returns a pointer to the buffer is successful or NULL if not.
As you have said that you could not use the ctype macros, I have defined ASCII (or any charset derived from ASCII) equivalent for the ones I have used.
#define TRUE 1
#define FALSE 0
inline int isupper(int c) {
return c >= 'A' && c <= 'Z'; // only for ASCII and derived
}
inline int isdigit(char c) {
return c >= '0' && c <= '9'; // guarantee per standard
}
inline int isblank(int c) {
return c == ' ' || c == '\t';
}
inline int isspace(int c) {
static const char spaces[] = " \t\r\n\v";
for(const char *s=spaces; *s != '\0'; s++) {
if (c == *s) return TRUE;
}
return FALSE;
}
char *get_string(char *buffer, int maxupper, int maxdigit, FILE *fd) {
char buf[16]; // any size >=2 will fit
char *cur = buffer;
int state = 0, uppersize=0, digitsize=0;
for (;;) { // allow lines longer than buf
if (NULL == fgets(buf, sizeof(buf), fd)) {
*cur = '\0'; // EOF: do not forget the terminating NULL
return state >= 1 ? buffer : NULL; // must have at least 1 char
}
for (char *b=buf; *b!='\0'; b++) {
switch(state) {
case 0: // spaces before first uppercase
if (isblank(*b)) break;
state++;
case 1: // first uppercase
if (! isupper(*b)) {
state = 5; // must read up to \n
break;
}
state++;
case 2: // process uppercase chars
if (! isupper(*b)) {
if (uppersize > 0) state++;
else {
state = 5; // must read up to \n
break;
}
}
else {
if (uppersize >= maxupper) {
state = 5; // must read up to \n
break;
}
*cur++ = *b;
uppersize++;
break;
}
case 3: // process digit chars
if (! isdigit(*b)) {
state++;
}
else {
if (digitsize >= maxdigit) {
state = 5; // must read up to \n
break;
}
*cur++ = *b;
digitsize++;
break;
}
case 4: // allow spaces after last digit
if ('\n' == *b) {
*cur = '\0';
return buffer;
}
if (! isspace(*b)) state++
break;
case 5: // on error clean end of line
if ('\n' == *b) return NULL;
}
}
}
}
Then in your code, you simply calls it that way:
...
printf("Enter flight code>\n");
if (NULL == get_string(flight->flightcode, 2, 4, stdin)) {
// process the error
...
}
...
First thing, realize that your question text is missing a question. Moreover, your question title makes no sense.
Anyway, here it is a possible, purposely very ugly, solution. Approach: you want to do X, so you write the code to do X. Let's start with scanline():
int scanline(char *dest, int dest_len)
{
int i = 0;
int ch;
while (1) {
// Read
ch = fgetc(stdin);
// Check
if (ch == EOF)
break;
if (ch == '\n')
break;
if (i >= dest_len - 1)
break;
// Use
dest[i] = ch;
++i;
}
dest[i] = 0;
// Is the string finished? Ok!
if (ch == '\n' || ch == EOF)
return 1;
// Otherwise discard the rest of the line. Not ok!
while (ch != '\n' && ch != EOF)
ch = fgetc(stdin);
return 0;
}
I know this is ugly, but I believe that it is helpful to clarify the three steps involved in file input: read, check, use. Note that it returns true if the line was up to the required number of characters (one less than the buffer size to accomodate for the terminator.
Then you want to check if:
scanline() is successful
there is at least one character.
character 0 is between 'A' and 'Z'
character 1 is between 'A' and 'Z'
character 2 is between '0' and '1'
character 3 is between '0' and '1'
character 4 is between '0' and '1'
character 5 is between '0' and '1'
Lets write the code for that:
int main(void)
{
flight_t flight;
while (1) {
printf("Enter flight code>\n");
if (!scanline(flight.flightcode, sizeof(flight.flightcode))) {
printf("Too many characters.\n");
continue;
}
int i = 0;
if (flight.flightcode[i] == 0) {
printf("Empty input.\n");
continue;
}
if (flight.flightcode[i] < 'A' || flight.flightcode[i] > 'Z') {
printf("Character %d is not upper case.\n", i);
continue;
}
i++;
if (flight.flightcode[i] == 0)
break;
if (flight.flightcode[i] < 'A' || flight.flightcode[i] > 'Z') {
printf("Character %d is not upper case.\n", i);
continue;
}
i++;
if (flight.flightcode[i] == 0)
break;
if (flight.flightcode[i] < '0' || flight.flightcode[i] > '9') {
printf("Character %d is not a digit.\n", i);
continue;
}
i++;
if (flight.flightcode[i] == 0)
break;
if (flight.flightcode[i] < '0' || flight.flightcode[i] > '9') {
printf("Character %d is not a digit.\n", i);
continue;
}
i++;
if (flight.flightcode[i] == 0)
break;
if (flight.flightcode[i] < '0' || flight.flightcode[i] > '9') {
printf("Character %d is not a digit.\n", i);
continue;
}
i++;
if (flight.flightcode[i] == 0)
break;
if (flight.flightcode[i] < '0' || flight.flightcode[i] > '9') {
printf("Character %d is not a digit.\n", i);
continue;
}
i++;
if (flight.flightcode[i] == 0)
break;
}
}
Some remarks:
in your code you set correct_code to 1 as soon as the first character was ok. If you want to loop through the characters you must check if there is an error and exit the loop.
don't use ASCII codes when you have the specific character literals available.
I suggest that you take my solution and, as an exercise fix it to be able to work with arbitrary MAX_FLIGHTCODE_LEN, and possibly with arbitrary number of letters and numbers. Of course MAX_FLIGHTCODE_LEN shall be equal to their sum!
Drop the useless requirement for not using <ctype.h>, and use also <stdbool.h>, which makes the programmer intention clearer.
(Edited to add this bit) I'm writing a program where it reads in a string of characters, then counts the frequency of each letter in the string and outputs the number of times each letter or non-letter occurs.
I was using tolower() but this was creating a problem.
I've changed it so I'm now using a loop to lower the case. Unfortunately the program is still crashing on me and I can't spot the problem.
I've run through each function to make sure I don't have anything loose. I've got the new line element being removed but when I run it through, I type in something as simple as "hello" and it crashes out.
#include <stdio.h>
#include <string.h>
void read(char letterfreq[], int STRSIZE)
{
printf("The capacity of the string array is %d\n", STRSIZE-1);
printf("Please enter your string:\n");
letterfreq[strlen(letterfreq) - 1] = '\0';
return;
}
void lowerCase(char letterfreq[], int STRSIZE)
{
}
}
void countFreq(int counter[], int COUNT, char letterfreq[], int STRSIZE)
{
int c = 0;
int i;
for(i = 0; i < STRSIZE; i++)
}
else
if((letterfreq[i] < 'a') || (letterfreq[i] > 'z'))
{
counter[COUNT-1]++;
}
}
}
return;
}
void printFreq(int counter[], int COUNT)
{
int c;
printf("Here is the letter frequency of your string:\n");
for(c = 0; c < COUNT; c++)
printf("There were also %d special characters in the string.\n", counter[26]);
return;
}
char promptReset(char reset)
{
printf("Would you like to calculate the letter frequency of another string? (Y/N)\n");
scanf("%c%*c", &reset);
do
{
readString(letterfreq, STRSIZE);
lowerCase(letterfreq, STRSIZE);
countFreq(counter, COUNT, letterfreq, STRSIZE);
printFreq(counter, COUNT);
reset = promptReset(reset);
}while(reset == 'y' || 'Y');
return(0);
}
Here(in function lowerCase):
letterfreq[i] = letterfreq[i] - 32;
you need to add 32 to convert the character into uppercase. Look at the ASCII table to know why.
Here(in function countFreq):
for(i = 0; i < STRSIZE; i++)
{
while(letterfreq[i] != '\0')
{
if(letterfreq[i] >= 'a' && letterfreq[i] <= 'z')
{
counter[c]++;
c++;
}
else
if((letterfreq[i] < 'a') || (letterfreq[i] > 'z'))
{
counter[COUNT-1]++;
}
}
}
the for loop doesn't make any sense. Remove it. Then, the while loop never ends as you don't increment i. You need i++; at the end of the while loop.
Here:
if(letterfreq[i] >= 'a' && letterfreq[i] <= 'z')
{
counter[c]++;
c++;
}
counter[c]++ won't count the frequency of each character. You need
counter[letterfreq[i]-'a']++;
for that. c is not needed. Remove it from the function.
Also, as #iharob has mentioned in a comment,
}while(reset == 'y' || 'Y');
should be
}while(reset == 'y' || reset == 'Y');
Fixed program:
#include <stdio.h>
#include <string.h>
const int COUNT = 27;
const int STRSIZE = 100;
void readString(char letterfreq[], int STRSIZE)
{
printf("The capacity of the string array is %d\n", STRSIZE-1);
printf("Please enter your string:\n");
fgets(letterfreq, STRSIZE, stdin);
letterfreq[strlen(letterfreq) - 1] = '\0';
//return; Not required
}
void lowerCase(char letterfreq[], int STRSIZE)
{
int i;
for(i = 0; i < STRSIZE; i++)
{
if(letterfreq[i] >= 'A' && letterfreq[i] <= 'Z')
{
letterfreq[i] = letterfreq[i] + 32; //Add instead of subtract
}
//i++; You already increment i from the loop
}
}
void countFreq(int counter[], int COUNT, char letterfreq[], int STRSIZE)
{
int i=0; // You forgot to initialize i with 0
while(letterfreq[i] != '\0')
{
if(letterfreq[i] >= 'a' && letterfreq[i] <= 'z')
{
counter[letterfreq[i]-'a']++;
}
else if((letterfreq[i] < 'a') || (letterfreq[i] > 'z')) //is always true; Use `else`
{
counter[COUNT-1]++;
}
i++; //You forgot this
}
//return; Not required
}
void printFreq(int counter[], int COUNT)
{
int c;
printf("Here is the letter frequency of your string:\n");
for(c = 0; c < COUNT-1; c++) //See change here too
{
printf("%c \t %d \n", c + 'a', counter[c]);
}
printf("There were also %d special characters in the string.\n", counter[26]);
//return; Not required
}
char promptReset(char reset)
{
printf("Would you like to calculate the letter frequency of another string? (Y/N)\n");
scanf("%c%*c", &reset);
return(reset);
}
int main()
{
char letterfreq[STRSIZE];
char reset = '\0'; //Always initialize variables to prevent unexpected behavior
int counter[COUNT];
printf("Welcome to the Letter Frequency program\n");
do
{
memset(counter,0,sizeof(counter)); //Initializing counter with zeros
memset(letterfreq,'\0',sizeof(letterfreq)); //Do the same for letterfreq
readString(letterfreq, STRSIZE);
lowerCase(letterfreq, STRSIZE);
countFreq(counter, COUNT, letterfreq, STRSIZE);
printFreq(counter, COUNT);
reset = promptReset(reset);
}while(reset == 'y' || reset == 'Y'); //Changed
return(0);
}
I got some help earlier fixing up one of the functions I am using in this program, but now I'm at a loss of logic.
I have three purposes and two functions in this program. The first purpose is to print a sentence that the user inputs backwards. The second purpose is to check if any of the words are anagrams with another in the sentence. The third purpose is to check if any one word is a palindrome.
I successfully completed the first purpose. I can print sentences backwards. But now I am unsure of how I should implement my functions to check whether or not any words are anagrams or palindromes.
Here's the code;
/*
* Ch8pp14.c
*
* Created on: Oct 12, 2013
* Author: RivalDog
* Purpose: Reverse a sentence, check for anagrams and palindromes
*/
#include <stdio.h>
#include <ctype.h> //Included ctype for tolower / toupper functions
#define bool int
#define true 1
#define false 0
//Write boolean function that will check if a word is an anagram
bool check_anagram(char a[], char b[])
{
int first[26] = {0}, second[26] = {0}, c = 0;
// Convert arrays into all lower case letters
while(a[c])
{
a[c] = (tolower(a[c]));
c++;
}
c = 0;
while(b[c])
{
b[c] = (tolower(b[c]));
c++;
}
c = 0;
while (a[c] != 0)
{
first[a[c]-'a']++;
c++;
}
c = 0;
while (b[c] != 0)
{
second[b[c]-'a']++;
c++;
}
for (c = 0; c < 26; c++)
{
if (first[c] != second[c])
return false;
}
return true;
}
//Write boolean function that will check if a word is a palindrome
bool palindrome(char a[])
{
int c=0, j, k;
//Convert array into all lower case letters
while (a[c])
{
a[c] = (tolower(a[c]));
c++;
}
c = 0;
j = 0;
k = strlen(a) - 1;
while (j < k)
{
if(a[j++] != a[k--])
return false;
}
return true;
}
int main(void)
{
int i = 0, j = 0, k = 0;
char a[80], terminator;
//Prompt user to enter sentence, store it into an array
printf("Enter a sentence: ");
j = getchar();
while (i < 80)
{
a[i] = j;
++i;
j = getchar();
if (j == '!' || j == '.' || j == '?')
{
terminator = j;
break;
}
else if(j == '\n')
{
break;
}
}
while(a[k])
{
a[k] = (tolower(a[k]));
k++;
}
k = 0;
while(k < i)
{
printf("%c", a[k]);
k++;
}
printf("%c\n", terminator);
//Search backwards through the loop for the start of the last word
//print the word, and then repeat that process for the rest of the words
for(j = i; j >= 0; j--)
{
while(j > -1)
{
if (j == 0)
{
for(k=j;k<i;k++)
{
printf("%c", a[k]);
}
printf("%c", terminator);
break;
}
else if (a[j] != ' ')
--j;
else if (a[j] == ' ')
{
for(k=j+1;k<i;k++)
{
printf("%c", a[k]);
}
printf(" ");
break;
}
}
i = j;
}
//Check if the words are anagrams using previously written function
for( i = 0; i < 80; i++)
{
if (a[i] == ' ')
{
}
}
//Check if the words are palindromes using previously written function
return 0;
}
I was thinking that perhaps I could again search through the array for the words by checking if the element is a space, and if it is, store from where the search started to the space's index-1 in a new array, repeat that process for the entire sentence, and then call my functions on all of the arrays. The issue I am seeing is that I can't really predict how many words a user will input in a sentence... So how can I set up my code to where I can check for anagrams/palindromes?
Thank you everyone!
~RivalDog
Would be better,if you first optimize your code and make it readable by adding comments.Then you can divide the problem in smaller parts like
1.How to count words in a string?
2.How to check whether two words are anagrams?
3.How to check whether a word is palindrome or not?
And these smaller programs you could easily get by Googling. Then your job will be just to integrate these answers. Hope this helps.
To check anagram, no need to calculate number of words and comparing them one by one or whatever you are thinking.
Look at this code. In this code function read_word() is reading word/phrase input using an int array of 26 elements to keep track of how many times each letter has been seen instead of storing the letters itself. Another function equal_array() is to check whether both array a and b (in main) are equal (anagram) or not and return a Boolean value as a result.
#include <stdio.h>
#include <ctype.h>
#include <stdbool.h>
void read_word(int counts[26]);
bool equal_array(int counts1[26],int counts2[26]);
int main()
{
int a[26] = {0}, b[26] = {0};
printf("Enter first word/phrase: ");
read_word(a);
printf("Enter second word/phrase: ");
read_word(b);
bool flag = equal_array(a,b);
printf("The words/phrase are ");
if(flag)
printf("anagrams");
else
printf("not anagrams");
return 0;
}
void read_word(int counts[26])
{
int ch;
while((ch = getchar()) != '\n')
if(ch >= 'A' && ch <= 'Z' || ch >= 'a' && ch <= 'z')
counts[toupper(ch) - 'A']++;
}
bool equal_array(int counts1[26],int counts2[26])
{
int i = 0;
while(i < 26)
{
if(counts1[i] == counts2[i])
i++;
else
break;
}
return i == 26 ? true : false;
}