Scanf preventing a C pragram from looping - c

I'm trying to write a program that takes Roman numerals as input and then converts them to decimal values. The user has to first declare how many Roman numerals they are going to input (either one or two).
I am using a for loop that repeats as many times as the number of Roman numerals. It either shouldn't loop if the there is only one numeral or if there are two it should loop twice because we need to take one letter as input at a time.
The issue I was having is that the scanf statement that is inside the for loop, keeps preventing the programme from looping. As soon as I removed the scanf and statically assigned the value then it worked perfectly fine. Then while trying to fix the issue I tried to print out the value scanf is returning by assigning it to a new variable, like char snf = scanf("%s", &numeral); and for some reason it started working exactly I wanted it to work. I have absolutely no idea why it is working now and why it was preventing the loop from looping before. Can anyone explain to me what's going on?
// A program to convert Roman Numerals to Decimals system.
#include <stdio.h>
int convert_numerals(char numeral){
switch(numeral){
case 'I':
return 1;
case 'V':
return 5;
case 'X':
return 10;
case 'L':
return 50;
case 'C':
return 100;
case 'D':
return 500;
case 'M':
return 1000;
default :
printf("\nError! You did not enter a valid numeral\n");
return 0;}}
int main(){
int Decimal_Val = 0; //Initializing the variable with 0 to avoid issues at check.
int Numeral_Count;
printf("How many characters does your Roman numerals have? 1 or 2\n");
scanf("%d",&Numeral_Count);
for (int i = 1; i < 1+Numeral_Count; ++i)
{
char numeral = 'O';
int converted_val;
printf("\n\nEnter numeral %d : ",i);
scanf("%s", &numeral); // The problematic line.
converted_val = convert_numerals(numeral);
if (Decimal_Val != 0)
{
if (Decimal_Val < converted_val)
{
Decimal_Val = converted_val - Decimal_Val;
}else{
Decimal_Val += converted_val;
}
}else{
Decimal_Val = converted_val;
}
}
printf("\nThe Roman numerals you entered are equal to %d in Decimals\n", Decimal_Val);
return 0;
}

Scanf can be a problematic function, especially with characters. Instead, try using fgets to read a line, then use the first character. If we break this out into a separate function. (Breaking problems down is crucial to solving complex problems in any programming language.)
char get_roman_numeral(const char *prompt, const char * error_msg) {
while (1) {
printf("%s: ", prompt);
char input[20] = {};
fgets(input, 19, stdin);
input[strcspn(input, "\n")] = '\0';
if (strlen(input) > 0) {
switch (input[0]) {
case 'i': case 'I':
case 'v': case 'V':
case 'x': case 'X':
case 'l': case 'L':
case 'c': case 'C':
case 'd': case 'D':
case 'm': case 'M':
return input[0];
default:
printf("%s\n", error_msg);
}
}
else {
printf("%s\n", error_msg);
}
}
}
Picking this apart, we loop indefinitely. Each time we print the prompt we provide, then read a line from stdin into the char buffer input which can hold 20 characters (one of them has to be the null terminating character).
input[strcspn(input, "\n")] = '\0';
This is going to find the first newline character in the input string and set it to '\0'. This effectively removes the newline character than fgets will include in the input string.
If the input string is then longer than 0 characters, we'll evaluate the first character. If it's a roman numeral, we return it. The function is done!
If it's either not a roman numeral, or the string is zero characters in length, we'll print the error message, and the loop starts over.
Hopefully looking at getting your input this way, without the problematic scanf will help you solve the bigger problem.

just decide to stop working for no apparent reason.
Below fails as "%s" attmeps to form a string and numeral is only big enoguh for the stirng "".
//char numeral = 'O';
//scanf("%s", &numeral); // The problematic line.
char numeral[100];
if (scanf("%99s", &numeral) == 1) {
// Success, continue and use `numeral`
converted_val = convert_numerals(numeral); will need to change too as that only handles 1 char.

Related

Nested switch() - case issue in C. Why are <ctype.h> functions not working the way I would like them to?

I have been trying to understand why this is not working properly for the past five hours.
The question explicitly asks for the use of switch() and no if-else (or of the likes) to count the number of words, lines, and characters in typed up text. And exit the program with Ctrl+D or Ctrl+Z.
Here, I deconstructed the counting by figuring different cases of whether the current typed input is whitespace or not, and from thereon, judging by the previous letter, whether it is justified to count it as an extra word, character, and/or line. ( input = punctuation, previous input= character --> add 1 to word count and 1 to character count ; if input = newline and previous input !=whitespace --> add one to line counter + one to word counter, etc.)
My code is the following:
int main() {
int letter = 0, prev_letter = 0, num_char = 0, num_words = 0, num_lines = 0;
printf("User, please provide any text you wish using letters, spaces, tabs, "
"and enter. \n When done, enter Ctrl+D or Ctrl+Z on your keyboard.");
while ((letter = getchar()) != 4 &&
letter != 26) // In ASCII, Ctrl+D is 4, and Ctrl+Z is 26
{
switch (isspace(letter)) {
case 0: // False = is not a whitespace
{
switch (
isalpha(prev_letter)) // checking to see if alphanumeric input or not
{
case 1:
switch (ispunct(letter)) {
case 1:
num_words++;
num_char++; // Punctuation considered as characters in this particular
// sub-exercise.
break;
}
break;
case 0:
num_char++;
break; // All other cases are just another character added in this case
// 0 (Not whitespace)
}
} break;
case 1: {
switch (letter) {
case 9: // 9 =Horizontal tab
{
switch (isspace(prev_letter)) {
case 0:
num_words++; // Assuming if not whitespace, then punctuation or
// character.
break;
default:
break;
}
} break;
case 32: // 32 = Space
{
switch (isspace(prev_letter)) {
case 0:
num_words++; // Assuming if not whitespace, then punctuation or
// character.
break;
default:
break;
}
} break;
case 13: // 13 = Carriage return
{
switch (isspace(prev_letter)) {
case 0:
num_words++;
num_lines++;
break;
default:
num_lines++;
}
} break;
case 10: // 13 = Line Feed
{
switch (isspace(prev_letter)) {
case 0:
num_words++;
num_lines++;
break;
default:
num_lines++;
}
} break;
default:
printf("Test2");
}
} break;
default:
break;
}
prev_letter = letter;
}
printf("Number of characters is: %d. \n", num_char);
printf("Number of words is: %d. \n", num_words);
printf("Number of lines is: %d. \n", num_lines);
return 0;
}```
It seems like isalpha(), ispunct(), isalnum() are not feeding properly my cases.
I have tried breaking it down to individual cases but when inputting text with tabs, spaces, and alphanumeric inputs, it fails to count words, characters, and lines properly.
What am I not seeing properly? Any pointers greatly appreciated.
That seems an odd assignment. Maybe you're misunderstanding it, but assuming you aren't, let me see if I can figure it out.
switch compares one variable to several values, so that would mean the first part is to determine a single value from several test functions.
A common use of switch is to check enum values, so you can start by defining an enum with the values you need. You're using isalpha(), ispunct(), and isspace() in your code, so I'll define those enum values.
enum chartype {
IS_ALPHA,
IS_PUNCT,
IS_SPACE,
IS_UNKNOWN
};
You can select an enum value without using if with the ? : operators.
enum chartype letter_chartype =
isalpha(letter) ? IS_ALPHA
: ispunct(letter) ? IS_PUNCT
: isspace(letter) ? IS_SPACE
: IS_UNKNOWN;
That lets you use a switch for each character type.
switch(letter_chartype) {
case IS_ALPHA:
...
break;
case IS_PUNCT:
...
break;
case IS_SPACE:
...
break;
default:
...
break;
}
That doesn't do your assignment for you, but I hope that helps give you some direction. I'm assuming you covered the ? : operators. If not, you might have to do something longer, trickier, or stupider (stupid often looks clever, be careful about that).
isalpha and similar return zero if the parameter is not letter or not zero if it is.
Use if statement not switch to test the condition. Using switch for logical data is not logical or practical.
use functions and write your own implementations of those functions using switch ... case
Example (using GCC extension case ranges - to be portable you need to have separate case for every letter or digit [like in myispace])
int myisalpha(unsigned char x)
{
switch(x)
{
case 'a' ... 'z':
case 'A' ... 'Z':
return 1;
}
return 0;
}
int myisdigit(unsigned char x)
{
switch(x)
{
case '0' ... '9':
return 1;
}
return 0;
}
int myisspace(unsigned char x)
{
switch(x)
{
case ' ':
case '\n':
case '\r':
case '\t':
case '\v':
case '\f':
return 1;
}
return 0;
}

Convert roman numerals to decimal numbers

I write the following code to convert roman numbers to decimal numbers using C language but it is not giving the correct answers. I have done dry run also. please help.
#include <stdio.h>
int main()
{
char ch;
int num, newnum=0, result=0;
printf("Enter number:");
scanf("%s", &ch);
while (ch != 'n') {
switch (ch) {
case 'i': num = 1; break;
case 'v': num = 5; break;
case 'x': num = 10; break;
case 'l': num = 50; break;
case 'c': num = 100; break;
case 'd': num = 500; break;
case 'm': num = 1000; break;
}
if (newnum > num) {
result = result - num;
newnum = num;
} else {
result = result + num;
newnum = num;
}
printf("Enter number:");
scanf("%s", &ch);
}
printf("%d", result);
}
main()
should be written
int main( void )
This is not just a stylistic nit - as of C99, implicit typing is no longer allowed.
scanf("%s", &ch);
is not what you want here - the s conversion specifier expects ch to point to the first element of an array of char, not a single char object. It tells scanf to read a sequence of non-whitespace characters and store them to a buffer, and it will write the zero terminator to the end of that buffer (i.e., to store a one-character string, you need a 2-element array of char). As written, scanf will write to both ch and the byte immediately following it in memory, which may or may not have an effect elsewhere.
To read a single character from standard input, either use
scanf( " %c", &ch ); // note leading blank in format string; this tells scanf
// to skip over any leading whitespace characters
or
int ch;
...
ch = getchar(); // getchar returns int, not char
You might want to walk through your conversion algorithm on paper a few times. What happens when you enter i followed by v?

Why does the string return "#" or mess up my "Z"?

char convertalphas(char s) {
switch (s){
case 'A':
return '0';
break;
case 'B':
return '1';
break;
case 'C':
return '2';
break;
case 'D':
return '3';
break;
case 'E':
return '4';
break;
case 'F':
return '5';
break;
case 'G':
return '6';
break;
case 'H':
return '7';
break;
case 'I':
return '8';
break;
case 'J':
return '9';
break;
case 'K':
return '10';
break;
case 'L':
return '11';
break;
case 'M':
return '12';
break;
case 'N':
return '13';
break;
case 'O':
return '14';
break;
case 'P':
return '15';
break;
case 'Q':
return '16';
break;
case 'R':
return '17';
break;
case 'S':
return '18';
break;
case 'T':
return '19';
break;
case 'U':
return '20';
break;
case 'V':
return '21';
break;
case 'W':
return '22';
break;
case 'X':
return '23';
break;
case 'Y':
return '24';
break;
case 'Z':
return '25';
break;
}
}
int main()
{
char astring[10];
int i = 0;
int flag = 0;
int startedalpha = 0;
//check if there is a digit input or not
int nodigit = 0;
char cell[10];
int col;
scanf( "%s", &astring );
for ( i = 0; i < 10; ++i )
{
if(astring[i] != '\0') {
//check whether letter is capital or small
if (astring[i] >= 65 && astring[i] <= 90)
{
startedalpha = 1;
//printf( "%c\n", astring[i] );
cell[i] = convertalphas(astring[i]);
printf("cell is %s\n", cell);
}
What im trying to do is to concatenate all of my conversions for a later use. when I put "AB" it returns "01#" and when I put "Z" it returns something else than "25". I don't know what is wrong but it is driving me crazy! I want to be able to input "ABZ" and it saves all of my values into the variable cell. For example, "ABZ" "0125"
Thanks!
I'm confused with this forum. Obviously, I posted the question because there is something I don't know and I'm not a guru as many of you! So, why would I get -2? I already posted what I tried. I thought it is about helping not being condescending!
Thanks for those who replied anyway!
Edit --
I converted my switch statement to int but now how can I concatenate the integers in variable cell?
One of things I noticed is that you have a char returning function, and is returning something else than a char, like '10'. It will return an unexpected value. You can, for example, set the return of this function to integer.
So it will look like this:
int convertalphas(char s) {
switch (s){
case 'A':
return 0;
case 'B':
return 1;
case 'C':
return 2;
case 'D':
return 3;
case 'E':
return 4;
return -1;
}
And then, change inside the loop in main function:
sprintf(auxvar, "%d", convertalphas(astring[i]);
for( jj = 0; jj < strlen(auxvar); jj++)
cell[i++] = auxvar[jj];
I hope it can help you somehow!
Good Luck.
According to the C Standard (6.4.4.4 Character constants)
...The value of an integer character constant containing more than one
character (e.g., 'ab'), or containing a character or escape sequence
that does not map to a single-byte execution character, is
implementation-defined.All
All return values of the function starting from '10' to '25' inclusively are imolementation defined and you can get the result that you did not expect to get.
You need to write a function that would be declared like
char * convertalphas( const char *s );
Or
char * convertalphas( char *dest, const char *source );
By the way it will be difficult to make the reverse conversion. For example what does "25" mean? Whether it is "CF" or "Z"?:)
Apart from the values returned by chars 'K' thru 'Z' (as mentioned by others) there are some other errors with the use of cell
Each character returned from your function is written into cell[] and then you print cell as a string. But there is no string terminator. You either need to fill cell[] with zeros first, or write a 0 into the next character
cell[i] = convertalphas(astring[i]);
cell[i+1] = 0;
But the problem with this is that cell[i] is skipped when the char is not a capital letter. You need to maintain a separate index into cell[]
cell[j++] = convertalphas(astring[i]);
cell[j] = 0;
One more problem is cell[] is not long enough for the final string terminator. You need to declare it as
char cell[11];
To summarize, your problem is:
A char function cannot return anything other than a single character. If you try to return something like '12', it just simply doesn't work. You need to rethink your algorithm. Implementing this using ints should be very straightforward.
Is this a homework assignment requiring the use of the switch statement? If not, and if you follow the advice to return int instead of char, a simple s-65 would do the trick, you don't need a 78-line switch.
Since cell is a char array, each element of cell can also only store a single character.
It is not very clear what you are trying to achieve, so I will try to comprehend it in two ways:
3.1. You want to store each character's value separately in cell.
Example: for input "ABZ", you want cell to be {0, 1, 25}.
This is easy. Change cell to an int array, and output them using %d format specifier in your printf.
3.2 You treat cell as a string rather than an array of characters, and you concatenate the string for every character's value.
Example: for input "ABZ", you want cell to be "0125", or in other words, {'0', '1', '2', '5'}. This means that you won't be able to differentiate between "ABZ" and, say, "AMF", as others have pointed out.
If this is what you want to achieve, firstly 10 elements is not enough for cell - each character must be stored separately, and you cannot store "25" as two characters in a single element of cell.
You can assign it with, say, 21 elements (2 max for each alphabet, one extra for the terminating '\0' byte). After each alphabet is converted to its value in int, implement a counter and some conditionals to fill cell one character at a time. Example, if an alphabet's value is stored in val:
char cell[21];
int i=0;
if(val < 10) {
cell[i] = val + '0';
i++;
} else {
cell[i] = val / 10 + '0';
i++;
cell[i] = val % 10 + '0';
i++;
}
I'll leave the implementation of this into a loop as an exercise for you...

Unable to read all characters(using getchar) inside for loop

Code:
//Program to find no of vowels
#include<stdio.h>
int main()
{
int count;char letter;int vowel=0;
for(count=0;count<10;count++)
{
letter=getchar();
switch(letter)
{
case 'a':
case 'e':
case 'i':
case 'o':
case 'u':vowel++;
}printf("Count:%d",count);
}
printf("NO of vowels is %d\n",vowel);
return 0;
}
Output:
a
a
s
d
f
NO of vowels is 2
The program reads only 5 characters and then displays the expected output. I tried printing the value of 'count' and by the end of loop, it incremented to 10. But,I am not able to read the number of characters(10) equivalent to my for loop condition. Please help.
I'm surprised you didn't notice from the printf("Count: %d\n"); line that the count increments by two for each letter of input. The getChar function gets all of the characters from the input text, including spaces and newlines. The simplest way to make your program behave as you expect is to increase the maximum count to 20:
for(count = 0; count < 20; count++)
{
letter = getchar();
// ...
Alternatives include using scanf and checking for reaching the end of text input, but your implementation is much safer.
Space ' ' is also a character. getchar reads a character at a time and hence reads ' ' too. Remove spaces from the input.
#include <stdio.h>
#include <ctype.h>
int main(){
int count=0, vowel=0;
char letter;
while(count < 10){
letter=getchar();
if(isgraph(letter)){
switch(tolower(letter)) {
case 'a':
case 'e':
case 'i':
case 'o':
case 'u':vowel++;
}
++count;//Counting only the case of the display characters that are not blank
}
}
printf("NO of vowels is %d\n",vowel);
return 0;
}

How to get a character ASCII value in a integer variable?

I'm new in C and I couldnt find the answer to my question in the forum.
The point is, I need to get a value of deck cards from the user. So it can spread from 2 to 10 and also be 'J', 'Q', 'K' or 'A'. That means it can be a integer or a character.
I'm trying to put it in an integer variable called "_val1". This work for any number from 0 to 10. I expected that if I typed a letter, _val1 would get the ASCII value of that character (wich I could use later for my pourposes). But instead _val1 geta value '0' and the letter is automatically passed to my next variable call (wich is _naipe1).
How can I solve that?
That means, how cam I use scanf to get either a integer value or the ASCII value of a character?
short int _val1, _val2;
char _naipe1, _naipe2;
printf("Qual a 1ยช carta?\n Valor:");
scanf(" %hd", &_val1);
printf("Valor 1 = %hd \n", _val1 );
printf(" Naipe:");
scanf(" %c", &_naipe1);
well, if I were you I'd try to simplify the problem:
get the ASCII value of the card representation from '2' to '9' and 'J','Q','K','A' ; there you can simply use a scanf("%c") or even better a getchar() operation.
then either you keep using the ASCII representation of your cards throughout your algorithm, or you can translate it using a mapping function such as:
int map(char card) {
switch (card) {
case '2':
case '3':
case '4':
case '5':
case '6':
case '7':
case '8':
case '9':
return card-'0';
case 'A':
return 1;
case 'J':
return 10;
case 'Q':
return 11;
case 'K':
return 12;
}
}
First, there are 52 cards to a typical poker deck, These are split into 4 suits: hearts, diamonds, spades and clubs. This kind of suggests that user input will be something like: 10s, Ad, 3c, etc (meaning 10 of spades, Ace of diamonds and 3 of clubs) So, not only must you determine the value of the individual card, you also must determine the suit.
This will not solve all of those requirements, but it will at least answer your most direct question, how to read an int or a char using scanf().
This will demonstrate that:
#include <stdio.h>
int main(int argc, char** argv)
{
int aNumber;
char aChar;
printf("\nEnter a number:");
scanf("%d", &aNumber);
printf("\nEnter a character:");
scanf("%c", &aChar);
printf("\nThe number entered is %d\n", aNumber);
printf("\nThe character entered is %c\n", aChar);
return 0;
}
You can also simply have all the values in a string such as
char cards[]={"Ad Js 10c 2c Qh"};
Then parse it using strtok(), then test each token for its ascii content, using functions like isdigit() or isalpha()
Note: you will have to map each card to a value to keep them straight, something like this abbreviated enum may work:
enum {
AD = 1, //start enum values at 1 for the diamonds suit
2D,
3D,
...//fill in rest of cards here
JC,
QC,
KC, // last card == 52, with the clubs suit
};
The reason your output from _val1 is 0 when entering a letter lies in the fact that you've declared _val1 as an short int. You should be using a char. Then you can assign and compare their ascii values.
char card;
int value;
scanf("%c", card);
if(card < 58 && card > 49)
value = card - 48;
else {
switch(card) {
case 'a': value = 1;
case '0': value = 10;
case 'j': value = 11;
case 'q': value = 12;
case 'k': value = 13;
default: printf("Must enter 0-9 (0 for 10 card), or a, j, q, k\n");
}
}
To read in "A", "2", "3", ... "10", "J",... "K", use fgetc() and strchr().
#include <ctype.h>
#include <stddef.h>
#include <stdio.h>
#include <string.h>
short GetCardRank(void) {
static const char rank[] = "A234567891JQK";
short val = -1;
int ch = fgetc(stdin);
while (isspace(ch)) ch = fgetc(stdin); // Skip leading white-space
char *p = strchr(rank, toupper(ch)); // Use toupper() to make case insensitive
if (ch != EOF && p != NULL && *p != '\0') {
short val = (short) (p - rank + 1);
if (val != 10) return val;
ch = fgetc(stdin);
if (ch == '0') return val;
val = 1; // Allow a lone '1' to act like an 'A'
}
ungetc(ch, stdin); // Put back unused char for next IO function
return val;
}
I'm trying to put it in an integer variable called "_val1". This work for any number from 0 to 10. I expected that if I typed a letter, _val1 would get the ASCII value of that character (wich I could use later for my pourposes). But instead _val1 geta value '0' and the letter is automatically passed to my next variable call (wich is _naipe1)
The problem is that the %d conversion specifier only recognizes strings of decimal digits (with an optional leading + or -) and will stop reading at the first non-digit character; if you type in something other than a digit, then the input operation will fail and that character will be left in the input stream.
Your best bet is to read your input as text, then convert it to a numerical value manually, something like the following:
#include <ctype.h>
#include <stdlib.h>
/**
* Reads a card's face value (2-10,J,Q,K,A) from standard input
* Returns 0 on error
*/
short get_card_value( void )
{
char buf[4]; // large enough to hold a 2-digit string plus newline plus 0 terminator
short val = 0;
if ( fgets( buf, sizeof buf, stdin ) != NULL )
{
char *chk;
short tmp = (short) strtol( buf, &chk, 0 );
if ( isspace( *chk ) || *chk == 0 )
{
if ( tmp >= 2 && tmp <= 10 )
val = tmp;
}
else
{
switch( tolower( *chk ) )
{
case 'j': val = 11; break;
case 'q': val = 12; break;
case 'k': val = 13; break;
case 'a': val = 11; break;
default: break;
}
}
}
// else read error
return val;
}
You'd call this as
val1 = get_card_value();
if ( val1 == 0 )
// error on input
This code doesn't do any length checking on input, so if you enter a card value of 1234567890, that won't be handled gracefully.
Don't use leading underscores in your variable names; names with leading underscores are reserved for the implementation.

Resources