CS50 PSET2 Readability 2022 - c

Okay! I'm really new at programing, so I don't really know whats going on... my program compiles just fine, but it only says 'Before Grade 1', and I saw with debug50 that float L and float S are not doing the math that they are supposed to do, and I have NO clue what is wrong
This is my code
`
#include <cs50.h>
#include <stdio.h>
#include <ctype.h>
#include <math.h>
#include <string.h>
int count_sentences(string);
int count_words(string);
int count_letters(string);
int letters;
int sentences;
int words;
int main(void)
{
// prompt user for text
string text = get_string("Text: ");
// calcutate the reading level
float L = 100 * (float) letters / words;
float S = 100 * (float) sentences / words;
int index = round(0.0588 * L - 0.296 * S - 15.8);
//output the result
if (index < 1)
{
printf("Before Grade 1\n");
}
else if (index > 16)
{
printf ("Grade 16+\n");
}
else
{
printf("Grade %i\n", index);
}
}
//string is an array of characters, just go through each letter and check if it is a char or not, and add to the counter
int letters = 0;
int count_letters(string text)
{
for (int i = 0; i < strlen(text); i++)
{
if (isalpha(text[i]) != 0)
{
letters++;
}
}
return letters;
}
//calculation for words is made by counting the number of spaces + 1
int words = 1;
int count_words(string text)
{
for (int i = 1; i < strlen(text); i++)
{
if (isspace (text[i]) != 0)
{
words++;
}
}
return words;
}
// sentences end with ./!/?, so just count them
int sentences = 0;
int count_sentences(string text)
{
for (int i = 0; i < strlen(text); i++)
{
if (text[i] == '.' || text[i] == '!' || text[i] == '?')
{
sentences++;
}
}
return sentences;
}
`
Thanks!

I actually answered a similar type of coding question a while back. Basically, what is missing is the derivation of the letter, word, and sentence count because in your current program, you do not call the counting functions even though they are defined. Just defining functions is not enough in C. Somewhere, they need to be called. In looking at your code, the most likely place would be right after your code prompts and receives a text string from the user.
// prompt user for text
string text = get_string("Text: ");
letters = count_letters(text); /* The functions need to be called to derive letters, words, and sentences */
words = count_words(text);
sentences = count_sentences(text);
There probably are other bits to test and refine, but you might want to start with that.

Related

Problem in output of CS50 week 2 Problem set readability

The code is ment to take user input, then compute how many words, letters and sentences are in the input. Then calculate the index.
index = 0.0588 * L - 0.296 * S - 15.8
Where L = average number of letters per hundred words and S is the average number of sentences per 100 words in the text.
if the index is in less than one print "below grade 1" and if it is greater than 16 print "greater than 16". and anything in between should be grade __ (where __ = the index number), For example, if index number = 12. Print grade 12.
In my case the code seems to only be showing the correct output for grades less than 1 and more than 16 and ignoring the other cases.
More details and inputs to the problem here - https://cs50.harvard.edu/x/2022/psets/2/readability/
#include <cs50.h>
#include <stdio.h>
#include <ctype.h>
#include <string.h>
#include <math.h>
int count_letters(string text);
int count_sentances(string text);
int count_words(string text);
int Grade_level(int i, int j, int k);
int main(void)
{
string text = get_string("Text = ");
int num_letters = count_letters(text);
int num_sentances = count_sentances(text);
int num_words = count_words(text);
int gradeLevel = Grade_level(num_letters, num_sentances, num_words);
if (gradeLevel < 1)
{
printf("Before Grade 1\n");
}
else if (gradeLevel > 16)
{
printf("Grade 16+\n");
}
else
{
printf("Grade level %d\n", gradeLevel);
}
}
int count_letters(string text)
{
int letters = 0;
for (int i = 0; i < strlen(text); i++)
{
if (isalpha(text[i]))
{
letters++;
}
}
return letters;
}
int count_sentances(string text)
{
int sentances = 0;
for (int i = 0; i < strlen(text); i++)
{
if (text[i] == '.'|| text[i] == '!' || text[i] == '?')
{
sentances++;
}
}
return sentances;
}
int count_words(string text)
{
int words = 1;
for (int i = 0; i < strlen(text); i++)
{
if (text[i] == ' ')
{
words++;
}
}
return words;
}
int Grade_level(int num_letters, int num_words, int num_sentances)
{
float index, L, S;
L = (num_letters/num_words) * 100;
S = (num_sentances/num_words) * 100;
index = (0.0588 * L) - (0.296 * S) - 15.8;
index = round(index);
return (int)index;
}

Mistake in CS50 Readability project with only 1 input, all others work

I seem to have made a mistake in my code but I can't find it.
All reading grades give me the correct grade, except for grade 7 which results in grade 8.
I assume it is a rounding error of some sort?
I tested the following piece of code with and without the round() in the last function.
Without it most of the grade levels are off, with the round() in there I only get an mistake a the grade7 level.
Where is my mistake?
#include <cs50.h>
#include <stdio.h>
#include <ctype.h>
#include <math.h>
#include <string.h>
//Prototypes
int count_letters(string text);
int count_words(string text);
int count_sentences(string text);
int get_score (string text);
//Main function
int main(void)
{
//Get user input
string text = get_string("Text: ");
//Grade user text
int i = get_score(text);
if(i < 1)
{
printf("Before Grade 1\n");
}
else if (i > 1 && i < 16)
{
printf("Grade %i\n", i);
}
else
{
printf("Grade 16+\n");
}
}
// Extra functions
int count_letters(string text)
{
// variables
int letters = strlen(text);
int total_letters = 0;
int characters = 0;
// Loop through text and count all non-letters
for(int i = 0; i < letters; i++)
{
if((text[i] < 65 || text[i] > 95) && (text[i] < 97 || text[i] > 122))
{
characters++;
}
}
// substract all non-letters from total chars and return.
total_letters = letters - characters;
return total_letters;
}
int count_words(string text)
{
// variables
int letters = strlen(text);
int spaces = 1;
// Loop through text and count all spaces
for(int i = 0; i < letters; i++)
{
if(text[i] == ' ')
{
spaces++;
}
}
return spaces;
}
int count_sentences(string text)
{
// variables
int letters = strlen(text);
int sentence = 0;
// Loop through text and count all sentences
for(int i = 0; i < letters; i++)
{
if(text[i] == 46 || text[i] == 33 || text[i] == 63)
{
sentence++;
}
}
return sentence;
}
int get_score (string text)
{
//variables
int letters = count_letters(text);
int words = count_words(text);
int sentence = count_sentences(text);
float index = 0;
// letters divided by words * 100
float L = 100 * letters / words;
// sentences divided by words *100
float S = 100 * sentence / words;
index = round(0.0588 * L - 0.296 * S - 15.8);
return index;
}
if((text[i] < 65 || text[i] > 95) && (text[i] < 97 || text[i] > 122)) is almost certainly a bug. You probably meant to be implementing isalpha, but you did it incorrectly. You meant to write:
if((text[i] < 'A' || text[i] > 'Z') && (text[i] < 'a' || text[i] > 'z')), which would have avoided the typo in which 95 was used instead of 90. Instead of this, though, you should just use the standard library and write:
if( ! isalpha(text[i]) ) ...
Using literals like 'A' instead of the magic number 65 makes the code more readable and helps avoid trivial mistakes like this.
There are quite a few issues with your code:
As #IrAM has mentioned in a comment, your if does not handle a score of 1. Moreover, you can simplify your if checks if you start from the other end, i.e. first check for greater than 16:
int main(void)
{
//Get user input
string text = get_string("Text: ");
//Grade user text
int i = get_score(text);
if(i > 16)
{
printf("Grade 16+\n");
}
else if (i > 0)
{
printf("Grade %i\n", i);
}
else
{
printf("Before Grade 1\n");
}
}
As #Gerhardh mentions, you are dividing two integers which forces the result to be an integer too. If at least one of the operands is casted to a float, the result is type-promoted to a float:
// letters divided by words * 100
float L = 100 * (float) letters / words;
// sentences divided by words *100
float S = 100 * (float) sentence / words;
Optimizations
You have three different functions for counting words, sentences and letters. Why three loops when you can do it in one loop? Plus an additional iteration for strlen() in each function. Write a Count struct like this:
struct Count
{
int letters;
int words;
int sentences;
int length;
};
Then have one function that returns this struct. Like #WilliamPursell mentions, using character literals instead of ASCII values makes code much more readable:
Count get_count(string text)
{
Count result = {0, 1, 0, 0};
result.length = strlen(text);
int characters = 0;
// Loop through text and count all non-letters
for(int i = 0; i < count.length; i++)
{
if((text[i] < 'A' || text[i] > 'Z') && (text[i] < 'a' || text[i] > 'z'))
{
characters++;
}
if(text[i] == ' ')
{
result.words++;
}
if(text[i] == '.' || text[i] == '!' || text[i] == '?')
{
result.sentences++;
}
}
// subtract all non-letters from total chars and return.
count.letters = count.length - characters;
return result;
}
This is what get_score() will change to:
int get_score (string text)
{
//variables
Count result = get_count(text);
float index = 0;
// letters divided by words * 100
float L = 100 * (float) result.letters / result.words;
// sentences divided by words *100
float S = 100 * (float) result.sentences / result.words;
index = round(0.0588 * L - 0.296 * S - 15.8);
return index;
}
Side Note: A '.' may always not necessarily mean the end of a sentence. It has other meanings like in an acronym or as ellipsis.

The output isn't what i am expecting, the code isn't printing any error and i dont know whats going wrong

Vigenere cipher
Takes in a text and output a cipher version of that text
the user inputs a keyword in the command line and a text for which the user wishes to encrypt. If you're familiar with the Ceasar cipher it's pretty much the same thing except a small minor change, instead of inputting the actual shift value we instead input a keyword and the letters in the keyword would represent the shift value instead.
#include <cs50.h>
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#include <ctype.h>
int shift(char c);
int main(int argc, string argv[])
{
//checks for alphabetic characters
if (argc == 2)
{
for (int i = 0, n = strlen(argv[1]); i < n; i++)
{
if (isalpha(argv[1][i]) == false)
{
printf("Usage: ./vigenere keyword. \n");
return 1;
}
}
//promting the user for text
string text = get_string("plaintext: ");
printf("ciphertext: ");
//Intializing variables
string key = argv[1];
int l = strlen(key);
for (int j = 0, k = 0, m = strlen(text); j < m; j++)
{
if (islower(text[j]))
{
//lower cap letter enter here
printf("%c", 'a' + ( text[j] - 'a' + shift(key[k % l]) ) % 26);
k++;
}
else if (isupper(text[j]))
{
//Upper caps letter enter here
printf("%c", 'A' + ( text[j] - 'A' + shift(key[k % l]) ) % 26);
k++;
}
else
{
//The rest whatever left enter here
printf("%c", text[j]);
}
}
printf("\n");
return 0;
}
else
{
printf("Usage: ./vigenere keyword. \n");
return 1;
}
}
//This function calculates the shift value per character
int shift(char c)
{
return ( islower(c) - 'a' );
}
the problem is that the output isn't what I expect it to be. For instance, if the keyword is 'b' and the text is 'hello' the output must be 'ifmmp' but it's not. I don't know what goes wrong.

Vigenere cipher in cs50

I'm working my way through an online class teaching me how to code. I'm very new to this and have been slowly making my way through this class. I've run into an issue with the vingenere cipher. It doesn't iterate the key through the whole input.
Edit: the key should iterate through the user input and when it reaches the end of the key, loop back and begin again. The key should also skip over any special character(!##" ",etc)
#include <stdio.h>
#include <cs50.h>
#include <string.h>
#include <stdlib.h>
#include <ctype.h>
int main (int argc, string argv[])
{
if(argc !=2)
{
printf("please put in command line argument: example - ./vigenere command\n");
return 1;
}
string key = argv[1];
int keylength = strlen(key);
for (int i=0;i<keylength; i++)
{
if(!isalpha(key[i]))
{
printf("please make sure command is letter only. Please no numbers or special characters!\n");
return 1;
}
}
string input = GetString();
for (int i=0, k=0; i<keylength; i++)
{
if(isalpha(input[i]))
{
if(isupper(input[i]))
{
input[i]=((input[i]-'A')+(key[k%keylength]))%26+'A';
}
else
{
if(islower(input[i]))
{
input[i]=((input[i]-'a')+(key[k%keylength]))%26+'a';
}
}
}
}
printf("%s\n",input);
return 0;
}
I know string is not normal, but it's included in the header to help with new students. I guess we learn more as the class progresses.
You didn't change k in your for loop. And indeed I don't think you need k at all. And your loop only iterate through the length of key instead of the length of input.
int inputlength = strlen(input);
for (int i = 0; i < inputlength; ++i) {
if (isupper(input[i]))
input[i] = ((input[i]-'A') + (key[i%keylength])) % 26 + 'A';
/* ... ^ Use i here */
}
Regarding the issue that when the key is b and input is A, you must adjust the key.
input[i] = ((input[i]-'A') + (key[i%keylength]-'a')) % 26 + 'A';
To skip input special characters,
int inputlength = strlen(input);
for (int i = 0, k = 0; i < inputlength; ++i) {
if (isupper(input[i]))
input[i] = ((input[i]-'A') + (key[(k++)%keylength])) % 26 + 'A';
/* ... ^^^ */
else if (islower(input[i]))
input[i] = ((input[i]-'a') + (key[(k++)%keylength])) % 26 + 'a';
}

Can't get a function call in a function to work properly

I'm writing a program to generate a string of random uppercase letters, then take user input of uppercase letters, along with a character form the user. For any instance of the user input letter in the random string, it replaces that letter with the character entered by the user.
For example, s1 = {BDHFKYL} s2 = {YEIGH} c = '*'
Output = BD*FK*L
The program was working correctly until I added the feature to ask the user to enter what character they would like to replace the letters.
The output is:
Please enter at least 2 capital letters and a maximum of 20.
HDJSHDSHDDS
HDJSHDSHDDS
Enter a character to replace occuring letters.
*
NWLRBBMQB
Would you like to enter another string?
Here's the code:
void fillS1(char x[]);
void fillS2(char x[], char y[], char z);
void strFilter(char a[], char b[], char c);
int main(int argc, const char * argv[])
{
char s1[42];
char s2[22];
char x = 0;
fillS2(s2, s1, x);
return 0;
}
void fillS1(char x[])
{
for (int i = 0; i < 40; i++)
x[i] = 'A' + random() % 26;
x[40] = (char)0;
}
void fillS2(char x[], char y[], char z){
char loopContinue = 0;
do {
int i = 0;
int capitalLetterCheck = 0;
printf("Please enter at least 2 capital letters and a maximum of 20.\n");
while (( x[i] = getchar()) != '\n' ) {
i++;
}
x[i] = '\0';
if (i < 3) {
printf("You need at least two letters\n");
}
else if (i > 21){
printf("You cannot have more than twenty letters\n");
}
for (i = 0; i < 20; i++) {
if ((x[i] >= 'a') && (x[i] <= 'z')) {
printf("You many only have capital letters.\n");
capitalLetterCheck = 2;
}
}
if (capitalLetterCheck != 2) {
for (i = 0; i < 20; i++) {
if ((x[i] >= 'A') && (x[i] <= 'Z')) {
puts(x);
fillS1(y);
printf("Enter a character to replace occuring letters.\n");
while ((z = getchar() != '\n')) {
}
strFilter(y, x, z);
break;
}
}
}
printf("Would you like to enter another string?\n");
gets(&loopContinue);
} while (loopContinue != 'n');
}
void strFilter(char a[], char b[], char c){
int i = 0;
int n = 0;
while (n < 20) {
for (i = 0; i < 40; i++) {
if (a[i] == b[n]){
a[i] = c;
}
}
i = 0;
n++;
}
puts(a);
}
Thank you.
first of all please try to make your code a little easier to read, and I'm not talking about indenting but about its flow.
Also, your example output seems to work fine since there was nothing to change in any string here...?
There are a few things you should keep in mind when coding :
give your variables and functions explicit names, espcecially if you are going to have someone read your code at some point
try to keep the flow of your code simple by making small functions when you have a specifig task to execute (get the user's input, generate a random string, etc.) as opposed to just writing most of it in imbricated loops
You could also have a look at scanf (man scanf) to get the user's input
Try allocating a buffer when you get the user's input instead of having a static one that may not be of the right size
It's very easy to write some pseudo-code and then translate it into C :
WHILE someCondition
Generate a random string
Get a string from the user
Get a character from the user
Find and replace
END
Here is an example of how you could have organised your code (don't use it though - no frees, no getting the user's input, etc.) :
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
char* generateString(void)
{
return "AEIOUYAEIOUY"; // In your implementation, this is where you'd generate the random string
}
char* getStringInput(void)
{
return "HELLO"; // In your implementation, this is where you'd get the user's string
}
char getCharInput(void)
{
return '*'; // In your implementation, this is where you'd get the user's character
}
char* findAndReplace(char* randomString, char* userString, char userChar)
{
int l1;
int l2;
int i;
int j;
char* output;
l1 = strlen(randomString);
l2 = strlen(userString);
output = (char*)malloc(sizeof(*output) * l1);
strcpy(output, randomString);
for (i = 0; i < l1; ++i)
{
for (j = 0; j < l2; ++j)
if (randomString[i] == userString[j])
output[i] = userChar;
}
return (output);
}
int main(int ac, char** av)
{
char* randomString;
char* userString;
char userChar;
char* outputString;
randomString = generateString();
userString = getStringInput();
userChar = getCharInput();
outputString = findAndReplace(randomString, userString, userChar);
printf("Result: %s\n", outputString);
// don't forget to free any allocated buffer
return (1);
}
How much debugging have you done? Try putting some printfs in your code to see what happens - when functions are called, what are your variable's values, etc.
Example :
void fillS1(char x[])
{
printf("-- entering fillS1, buffer value: %s\n", x);
for (int i = 0; i < 40; i++)
x[i] = 'A' + random() % 26;
x[40] = (char)0;
printf("-- leaving fillS1, buffer value: %s\n", x);
}
(be careful about what's in your buffer before you use printf)
This should tell you pretty quickly what's going wrong.
For example, try checking the value of "c" in strFilter when it's called, and have a second look at how you get the user's input.

Resources