I'm relatively new to C programming and I am trying to code a program that reads in a user's marks and assign a grade to the mark inputted.
I have used the getline() function to acquire user input. Below is my code.
# include <stdio.h>
# include <stdlib.h>
# include <string.h>
# include <ctype.h>
int getMark();
void display(char grade);
char convert(int mark);
int getMark() {
do {
int marks;
char * buffer;
char characters;
size_t bufsize = 16;
buffer = (char *)malloc(bufsize * sizeof(char));
if( buffer == NULL) {
perror("Unable to allocate buffer");
exit(1);
}
printf("Enter the score (0 - 100)\nOr just press the enter key to quit ==> ");
characters = getline(&buffer, & bufsize, stdin);
int i;
long check;
for (i = 0; i < sizeof(buffer); i++) {
if (buffer[i] == '\n' && i != 0) {
buffer[i] = '\0';
}
if (isdigit(buffer[i]) || buffer[i] == '\0') {
check++;
}
}
if (buffer[0] == '\n') {
return -1;
} else if (check == strlen(buffer) - 1) {
marks = atoi(buffer);
} else {
printf("Please enter an integer.\n");
continue;
}
if (marks >= 0 && marks <= 100) {
return marks;
} else {
printf("Sorry, your marks can only be between 0 to 100.\n");
continue;
}
} while (1);
}
void display(char grade) {
printf("The grade for the input score is %c \n", grade);
}
char convert(int mark) {
mark /= 10;
char grade;
switch(mark) {
case 0:
case 1:
case 2:
case 3:
grade = 'F';
break;
case 4:
grade = 'E';
break;
case 5:
grade = 'D';
break;
case 6:
grade = 'C';
break;
case 7:
grade = 'B';
break;
case 8:
case 9:
case 10:
grade = 'A';
}
return grade;
}
int main() {
int marks;
for (;;) {
marks = getMark();
if (marks == -1) {
break;
}
display(convert(marks));
}
return 0;
}
When I press Enter, the program exits, as expected.
However, when an integer is entered, for example 12, the output will always be Please enter an integer.
Can anyone help me on this? Thanks!
char * buffer;
...
for (i = 0; i < sizeof(buffer); i++) {
if (buffer[i] == '\n' && i != 0) {
sizeof(buffer) returns the size of a pointer to char (4 or 8 bytes depending on the architecture), not the length of the string, in consequence, you are reading outside of the bounds of the array when 12 is entered.
Change to strlen() or better yet, use the result of getline:
On success, getline() and getdelim() return the number of characters
read, including the delimiter character, but not including the
terminating null byte ('\0').
Related
Given an integer n, the program has to delete each word, that contains the n number of vowels.
The string is read from a test.txt file, which contains the following:
Astazi nu este maine.
Currently my program contains a count1 function, that counts the number of characters and vowels for each word in the string.
How can I use the data from count1 function as a refference when typing in the n vowels to delete the needed words then print the updated string?
I have an idea, which I'm unsure how to implement. In count1 we already count the number of vowels per each word, so, given n by the user we check if this number is equal to v in the count1 function and so we do int num_of_words++, then we do a loop, which prints out the needed words, until num_of_words=0
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
void count1 (char* str)
{
for (int i = 0;;)
for (int v = 0, w = i;;)
{
int len;
char c = str[i++];
switch (c)
{
case 'A':
case 'E':
case 'I':
case 'O':
case 'U':
case 'a':
case 'e':
case 'i':
case 'o':
case 'u':
v++;
default:
continue;
case ' ':
case '\t':
case '\n':
case '\0':
len = i - 1 - w;
printf("'%.*s': %d characters, %d vowels\n", len, str+w, len, v );
if (c)
break;
else
return;
}
break;
}
}
void count2 (char* str, int n)
{
char line2[128];
int ls=strlen(str);
for (int i = 0;;)
for (int v = 0, w = i;;)
{
int len;
char c = str[i++];
switch (c)
{
case 'A':
case 'E':
case 'I':
case 'O':
case 'U':
case 'a':
case 'e':
case 'i':
case 'o':
case 'u':
v++;
default:
continue;
case ' ':
case '\t':
case '\n':
case '\0':
for(int k = 0; str[k] != '\0'; k++)
{
if (k == 0 || isspace(str[k]))
{
if(v==n)
{
strcat(line2, str+1);
}
}
}
printf("%s ", line2);
if (c)
break;
else
return;
}
break;
}
}
int main()
{
FILE *fp;
char line[128];
int c=0, count[26]= {0}, x;
int n;
fp = fopen("test.txt", "r");
fscanf(fp, "%[^\n]", line);
fclose(fp);
printf("%s\n\n", line);
while (line[c] != '\0')
{
if (line[c] >= 'a' && line[c] <= 'z')
{
x = line[c] - 'a';
count[x]++;
}
c++;
}
for (c = 0; c < 26; c++)
{
printf("%c occurs %d times.\n", c + 'a', count[c]);
}
printf("\n");
count1(line);
printf("\nInsert n: ");
scanf("%d", &n);
count2(line, n);
return 0;
}
If you have a string str that consists of separate words, separated one from another by ' ' or '\n' or '\t', and you want to have a string that contains all the words in str that satisfy some condition, it will be a bit difficult to program it such that it will be "in-place", i.e. to change str to the desired string, without using a "helper array" of some sort.
Instead, I would recommend to create a new char array with the same size (say str2), and every time you find a word that satisfies the condition (the condition can be for example: doesn't have 1 vowel), you copy the word that you found from str to str2.
Something like this:
char str[128];
// read from file to str using fscanf
char str2[128];
for (int c = 0; str[c] != '\0'; ++c)
{
if (c == 0 || isspace(str[c]))
{
if (! is_1_vowel[str+1]) // if it doesn't have exacly one vowel
{
// copy the word from str to str2
strcat_word(str2, str+1); // a user-defined adapted version of strcat that will copy from src to dest only till src reaches a space character or '\0'
}
}
}
I'm assuming here that is_1_vowel will be a function that goes over a single word (and not the whole line or file), and returns 1 if it satisfies the condition (has 1 vowel), and returns 0 otherwise.
Here's my final solution
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
void count1 (char* str)// count numbers of vowels for each word
{
for (int i = 0;;)
for (int v = 0, w = i;;)
{
int len;
char c = str[i++];
switch (c)
{
case 'A':
case 'E':
case 'I':
case 'O':
case 'U':
case 'a':
case 'e':
case 'i':
case 'o':
case 'u':
v++;
default:
continue;
case ' ':
case '\t':
case '\n':
case '\0':
len = i - 1 - w;
printf("'%.*s': %d characters, %d vowels\n", len, str+w, len, v );
if (c)
break;
else
return;
}
break;
}
}
void print_x(char* str, int n)
{
char* tmp;
unsigned int cnt = 0, stat = 0;
const char aeiou[] = "AEIOUaeiou";
while(*str)
{
switch(stat)
{
case 0://the word did not start
if (!isalpha(*str))
{
putchar(*str);
break;
}
stat = 1;
tmp = str;
cnt = 0;
case 1://the word started
if (strchr(aeiou, *str))
{
cnt++;
break;
}
if (! isalpha(*str))
{
if (cnt != n)
while(tmp <= str) putchar(*(tmp++));
else putchar(*str);
stat = 0;
}
} // end switch
++str;
}
if (stat)
{
--str;
if (cnt != n) while(tmp <= str) putchar(*(tmp++));
}
}
int main()
{
FILE *fp;
char line[128], line2[128];
int c=0, count[26]= {0}, x;
int n,a;
int i,j;
fp = fopen("test.txt", "r");
fscanf(fp, "%[^\n]", line);
fclose(fp);
printf("%s\n\n", line);
while (line[c] != '\0')
{
if (line[c] >= 'a' && line[c] <= 'z')
{
x = line[c] - 'a';
count[x]++;
}
c++;
}
for (c = 0; c < 26; c++)
{
printf("%c occurs %d times.\n", c + 'a', count[c]);
}
for (i = 0; i < 26; ++i)
{
for (j = i + 1; j < 26; ++j)
{
if (count[i] < count[j])
{
a = count[i];
count[i] = count[j];
count[j] = a;
}
}
}
printf("\n\n");
for (c = 0; c < 26; c++)
{
printf("%c occurs %d times.\n", c + 'a', count[c]);
}
printf("\n");
count1(line);
printf("\nInsert n: ");
scanf("%d", &n);
if (!(fp = fopen("./test.txt", "r")))
{
printf("unable open file\n");
return 1;
}
while (fgets(line, 128, fp))
print_x(line, n);
fclose(fp);
return 0;
}
How do I keep re-asking the user to enter a valid input that is an integer? I know how to validate an integer range input, but I don't know how to validate a non-integer input when prompted to enter an integer in C.
#include <stdio.h>
int main() {
int menu;
while(true) {
printf("Choose a menu (1-4):\n");
do {
scanf("%d", &menu);
} while(menu < 1 || menu > 4);
if(menu == 1) {
printf("menu 1\n");
} else if(menu == 2) {
printf("menu 2\n");
} else if(menu == 3) {
printf("menu 3\n");
} else if(menu == 4) {
printf("menu 4, exit\n");
break;
}
}
return 0;
}
Any help would be appreciated, I'm sorry if this is a duplicate as everytime I try to search the solution, it's on another language or the thread is asking "how to validate integer input range" instead of non-integer input.
if you want to know when the user has given a non-integer input, a way of doing that is as follows:
char number; /* assuming you'll only need numbers 0-9 */
int menu;
while (true)
{
scanf("%c",&number);
if (number >= '0' && number <= '9')
break;
else
printf("The input is not an integer\n");
}
menu = number - '0' ;
/* write rest of the code here */
If the input is 1 - 999, you can use this:
char *s = malloc(sizeof(char)*4);
while (true)
{
scanf("%s", s);
is_int = true;
menu = 0;
if (s[3] != '\0')
{
printf("integer bigger than 999 not allowed, input again\n");
continue;
}
for (int itr = 0; s[itr] != '\0'; itr++)
if (s[itr] >= '0' && s[itr] <= '9')
{
menu = menu*10 + s[itr]-'0';
}
else
{
is_int = false;
printf("not a valid integer, input again\n");
break;
}
if (is_int && menu != 0)
break;
}
Separate input from parsing.
Consider a helper function. Use fgets() to read, then parse.
// return 0 on success, EOF on end of file
int read_int(const char *prompt, int min, int max, &val) {
#define LINE_N 100
while (1) {
fputs(prompt, stdout);
fflush(stdout);
char buf[LINE_N];
if (fgets(buf, sizeof buf, stdin) == NULL) {
return EOF;
}
char *endptr;
errno = 0;
long val = strtol(buf, &endptr, 0);
// Validate an integer was read and if it is in range
if (endptr > buf && errno==0 && val >= min && val <= max) {
return (int) val;
}
}
}
Usage
int menu;
if (get_int("Choose a menu (1-4):\n", 1, 4, &menu) == EOF) {
printf("Input closed\n");
return 0;
}
if(menu == 1) {
...
For example the user entered this text : How arrree yooou
output ==> the most frequent vowel is : o
the most frequent consonants is : r
Not : Here 'e' also a duplicate, but the most frequent one is 'o', I am looking for the most frequent
/* Initializes frequency of all characters to 0 */
for(i=0; i<MAX_CHARS; i++)
{
freq[i] = 0;
}
/* Finds frequency of each characters */
i=0;
while(str[i] != '\0')
{
ascii = ((int)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');
vowels++;
freq[ascii] += 1;
i++;
}
/* Finds maximum frequency */
max = 0;
for(i=0; i<MAX_CHARS; i++)
{
if(freq[i] > freq[max])
max = i;
}
printf("Maximum occurring character is '%c' = %d times.", max, freq[max]);
return 0;
}
Since I was explicitly asked in the comments, you could do something like:
#include <stdio.h>
#include <string.h>
#include <ctype.h>
#include <assert.h>
struct max {
int count;
int value;
};
/* CAUTION: this incorrectly reports that '\0' is a vowel.
exercise for the reader. */
int isvowel(int c)
{
return strchr("aeiou", tolower(c)) != NULL;
}
int isconsonant(int c)
{
return isalpha(c) && ! isvowel(c);
}
int main(void)
{
int c;
struct max consonant = {0}, vowel = {0};
int count[256] = {0};
while( ( c = fgetc(stdin)) != EOF ) {
assert( c < 256 );
count[c] += 1;
if( isvowel(c) && count[c] > vowel.count ) {
vowel.count = count[c];
vowel.value = c;
} else if( isconsonant(c) && count[c] > consonant.count ) {
consonant.count = count[c];
consonant.value = c;
}
}
printf("%c appears %d time%s, %c appears %d time%s\n",
vowel.value, vowel.count, vowel.count > 1 ? "s" : "",
consonant.value, consonant.count, consonant.count > 1 ? "s" : "" );
return 0;
}
Here's a short solution I had fun assembling.
#include <stdio.h>
#include <ctype.h>
#include <string.h>
void get_most_frequent(int count[], const char const* set, char* letter, int *number)
{
*letter = '\0';
*number = 0;
for(int i=0; set[i]; ++i)
{
*letter = (count[set[i]-'a'] > *number)? set[i] : *letter;
*number = (count[set[i]-'a'] > *number)? count[set[i]-'a'] : *number;
}
}
int main(void) {
const char str[] = "My sample text, that Contains many vowels And consanants of all shapes and sizes; along with UPPER and LOWER cases.";
const char vowels[] = "aeiou";
const char cnsts[] = "bcdfghjklmnpqrstvwxyz";
int max;
char letter;
int count[26] = {};
for(int i=0; str[i]; ++i)
{
count[tolower(str[i])-'a'] += !!isalpha(str[i]);
}
// Most frequent vowel
get_most_frequent(count, vowels, &letter, &max);
printf("Vowel %c appears %d times\n", letter, max);
// Most frequent consonant
get_most_frequent(count, cnsts, &letter, &max);
printf("Consonant %c appears %d times\n", letter, max);
return 0;
}
Output
Success #stdin #stdout 0s 4532KB
Vowel a appears 13 times
Consonant s appears 11 times
Since you've already figured out the most frequent vowel/ consonant, you might pass it to a switch case like this:
switch(c)
{
case 'a':
case 'e':
case 'i':
case 'o':
case 'u':
case 'A':
case 'E':
case 'I':
case 'O':
case 'U':
printf("\nThe most frequent vowel is: %c", c);
break;
default:
printf("\nThe most frequent consonant is: %c", c);
}
In case if you just want to know whether the most repeated character is a vowel/ consonant, the switch case could do the job. I haven't cheked your entire code, but if here:
printf("Maximum occurring character is '%c' = %d times.", max, freq[max]);
max contains the most frequent character in your string, you could simply pass max to the switch case and print the output from the switch case itself.
What I mean by this is, I'm writing a program where the user inputs a string along with a '*' character and any number, and the program multiplies the string the user entered by that number
Please enter a string you'd like multiplied: horse*3
output:
horsehorsehorse
What I'd like to do is, if that string happens to be a number, then multiply the two numbers like you normally would
Please enter a string you'd like multiplied: 2*2
output:
4
I'm putting my code down below.
int multStr(char *strMult) //strMult is character array for initial input
{
//printf("\nRedirected to here!\n");
printf("Please enter a string and number you'd like to multiply by: ");
scanf(" %[^\n]s", strMult); //User scans in the string intended for multiplication
char string[255]; //Character array for the string part
char number[255]; //Character array for the number the string is to be multiplied by
int counter=0;
int i;
for(i=0;strMult[i] != '*'; i++)
{
string[i] = strMult[i];
counter++;
}
if(string[i] >= '0' && string[i] <= '9')
{
int strNum = myAtoi(string);
printf("String: %d", strNum);
}
else
{
printf("String: %s\n", string);
}
counter++;
for(i=0; strMult[counter] != '\0'; counter++, i++)
{
number[i] = strMult[counter];
}
//number[i+1] = '\0';
printf("Number: %s\n", number);
int num = myAtoi(number);
//printf("Number after convert: %d\n", num);
for(i=0; i<num; i++)
{
printf("%s", string);
}
printf("\n");
return(0);
}
strMult is a char array I called from main. I was not able to use stdlib, so I created my own Atoi function
You have a couple issues here. The first thing you're doing is splitting the string into two char[]s string and number. You seem to do that ok. The next thing you need to do is to check that string is a number.
if(string[i] >= '0' && string[i] <= '9')
{
int strNum = myAtoi(string);
printf("String: %d", strNum);
}
else
{
printf("String: %s\n", string);
}
This is what you're using, but note that it has no consequence to the rest of the program. Your number is local to the if block, and then it goes away, so you cannot use it later. What you want to do, is make the number available later.
int isNumber = 1;
int strNum;
for(int k = 0; k < i; k++){
if( string[k] >= '0' && string[k]<='9' ){
//continue checking
}else{
isNumber=0;
}
}
if (isNumber==1){
strNum = myAtoi(string);
} else{
// print your string or some such.
}
Note that I check all of the characters in the string, and not just the '*' which is the character at the i'th position. That way, if somebody enters horse42*2, it will think it is a string.
Also, you can improve this by checking the values as you separate out the number.
Regular expressions are a useful tool for parsing user input. Reading unconstrained user input with scanf is dangerous because unexpectedly long input will overflow the buffer.
Here is an example that use getline for reading input safely and regular expressions to sort out the format for the user input.
#include <regex.h>
#include <stdio.h>
#include <stdlib.h>
#define TERM "(([[:digit:]]+)|([[:alpha:]]+))"
void string_by_number(int number, char *string)
{
printf("number %d string %s\n", number, string);
for (int i = 0; i < number; ++i) {
printf("%s", string);
}
printf("\n");
}
int main(int argc, char *argv[])
{
regex_t re;
regmatch_t capture[7];
if (regcomp(&re, TERM "[[:space:]]*[*][[:space:]]*" TERM, REG_EXTENDED)) {
fprintf(stderr, "error in regular expression pattern\n");
return 1;
}
char *line = NULL;
size_t line_size = 0;
printf("Please enter a string you'd like multiplied: ");
while (0 < getline(&line, &line_size, stdin)) {
if (regexec(&re, line, 7, capture, 0)) {
printf("Unexpected input '%s'\n", line);
continue;
}
int format = 0;
char *lhs_string = NULL;
int lhs_number;
char *rhs_string = NULL;
int rhs_number;
if (capture[2].rm_so >= 0) {
format ^= 0x01;
lhs_number = strtol(&line[capture[2].rm_so], NULL, 10);
}
else {
lhs_string = &line[capture[3].rm_so];
line[capture[3].rm_eo] = '\0';
}
if (capture[5].rm_so >= 0) {
format ^= 0x02;
rhs_number = strtol(&line[capture[5].rm_so], NULL, 10);
}
else {
rhs_string = &line[capture[6].rm_so];
line[capture[6].rm_eo] = '\0';
}
switch (format) {
case 0x00: printf("Can't do 'string' * 'string'\n"); break;
case 0x01: string_by_number(lhs_number, rhs_string); break;
case 0x02: string_by_number(rhs_number, lhs_string); break;
case 0x03: printf("%d\n", lhs_number * rhs_number); break;
}
printf("\nPlease enter a string you'd like multiplied: ");
}
free(line);
regfree(&re);
return 0;
}
I've got this homework assignment where we get the user to enter the amount of lines of strings they desire, they then proceed to enter them which gets stored in a 2D Array (thus creating an array of strings). Then a switch case menu will be displayed which should
Search a character entered by the user, returns the amount of times the character occurred in the array
Search a word entered by the user, returns the amount of times the word occurred in the array
Have the user enter a specified word length and return the amount of times words of the specified length occur.
I have a couple problems with my code. The program runs without errors from the compiler. The searchByCharacter function works fine but the searchByWord only returns a value of 0 regardless of any word inputted and nothing happens after I input a number for the searchByLength function. The program freezes after I enter a length once I select the searchByLength function. I've been at this for a while and I don't know where I'm going wrong.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define MAX_LINE_LENGTH 80
#define MAX_LINES 10
#define WORD_LENGTH 20
void readText(char text[][MAX_LINE_LENGTH], int n)
{
int i;
printf("Enter %d number of lines:\n", n);
for(i = 0; i < n; i++)
{
scanf(" %[^\n]s", text[i]);
}
}
int searchByCharacter(char text[][MAX_LINE_LENGTH], int n, char c)
{
int i, charCount = 0, j = 0;
for(i = 0; i < n; i++)
{
j = 0;
while(text[i][j] != '\0')
{
if(text[i][j] == c)
{
charCount++;
}
j++;
}
}
return charCount;
}
int searchByWord(char text[][MAX_LINE_LENGTH], int n, char * keyword)
{
int i, wordCount = 0;
for(i = 0; i < n; i++)
{
int j = 0;
int lengthOfWord = 0;
char wordCheck[WORD_LENGTH];
char * currentLine = text[i];
while(currentLine[j] != '\0')
{
if (currentLine[j] == ' ' || currentLine[j] == '\n' || currentLine[j] == ',' || currentLine[j] == '.' ||
currentLine[j] == ';')
{
wordCheck[lengthOfWord] = '\0';
int matchingWord = strcmp(wordCheck, keyword);
if(matchingWord == 0)
{
wordCount++;
}
lengthOfWord = 0;
j++;
continue;
}
wordCheck[lengthOfWord] = currentLine[n];
lengthOfWord++;
j++;
}
}
return wordCount;
}
int searchByLength(char text[][MAX_LINE_LENGTH], int n, int wordLen)
{
int i, lengthCount = 0;
for(i = 0; i < n; i++)
{
int lengthOfWord = 0;
int j = 0;
char * currentLine2 = text[i];
while(currentLine2[j] != '\0')
{
if (currentLine2[j] == ' ' || currentLine2[j] == '\n' || currentLine2[j] == ',' || currentLine2[j] == '.' ||
currentLine2[j] == ';')
{
if(lengthOfWord == wordLen)
{
lengthCount++;
}
lengthOfWord = 0;
n++;
continue;
}
lengthOfWord++;
n++;
}
}
return lengthCount;
}
int main(void)
{
char textInput[MAX_LINES][MAX_LINE_LENGTH];
printf("Enter number of lines (<10): ");
int textLines = 0;
scanf("%d", &textLines);
while(textLines < 1 || textLines > 10)
{
printf("Invalid Input.\n");
printf("Enter number of lines (<10): ");
scanf("%d", &textLines);
}
if(textLines >= 1 && textLines <= 10)
{
readText(textInput, textLines);
int menuActive = 1;
while(menuActive)
{
printf("\nText Analysis\n----\n");
printf("1-Search By Character\n2-Search By Word\n3-Search By Length\n0-Quit\nPlease enter a selection: ");
int selection;
scanf("%d", &selection);
switch(selection)
{
case 0:
menuActive = 0;
break;
case 1:
printf("Selected 1\n");
printf("Enter a character to search: ");
char characterSearch;
scanf(" %c", &characterSearch);
int characterwordCount = searchByCharacter(textInput, textLines, characterSearch);
printf("\nNumber of occurence of %c = %d", characterSearch, characterwordCount);
break;
case 2:
printf("Selected 2\n");
printf("Enter a word to search: ");
char wordSearch[MAX_LINE_LENGTH];
scanf(" %s", wordSearch);
int lengthwordCount = searchByWord(textInput, textLines, wordSearch);
printf("\nNumber of occurence of %s = %d", wordSearch, lengthwordCount);
break;
case 3:
printf("Selected 3\n");
printf("Enter search length: ");
int wordLength;
scanf(" %d", &wordLength);
int wordLengthwordCount = searchByLength(textInput, textLines, wordLength);
printf("Number of words with length %d = %d", wordLength, wordLengthwordCount);
break;
default:
printf("Invalid Input.\n");
}
}
printf("You Have Quit!\n");
}
return 0;
}