CS50 Week 2 Substitution: Converting a string to uppercase - c

I'm trying to convert a string to all uppercase using below:
string key = argv[1];
for (int i = 0; i < len; i++)
{
if islower(key[i])
{
toupper(key[i]);
}
}
I'm receiving:
substitution.c:59:13: error: ignoring return value of function declared with pure attribute [-Werror,-Wunused-value]
toupper(key[i]);
Could anyone help me understand what this means?
The other part of the code is listed below if that's relevant.
int main(int argc, string argv[])
{
// strlen = 26
int len = strlen(argv[1]);
if (len != 26)
{
printf("Usage: ./substitution key\n");
return 1;
}
// input must be single string
if (argc != 2)
{
printf("Usage: ./substitution key\n");
return 1;
}
// check each char as letter
for (int i = 0; i < len; i++)
{
if (!isalpha(argv[1][i]))
{
printf("Usage: ./substitution key\n");
return 1;
}
}
// check for duplicates
for (int i = 0; i < len; i++)
{
for (int j = i + 1; j < len; j++)
{
if (toupper((argv[1][i])) == toupper((argv[1][j])))
{
printf("Usage: ./substitution key\n");
return 1;
}
}
}
Thanks!
Trying to convert a string to all uppercase but ran into an error that I can't understand. Thanks!

From the man page:
DESCRIPTION:
These functions convert lowercase letters to uppercase, and vice versa.
If c is a lowercase letter, toupper() returns its uppercase equivalent, if an uppercase representation exists in the current locale. Otherwise, it returns c. The toupper_l() function
performs the same task, but uses the locale referred to by the locale handle locale.
If c is an uppercase letter, tolower() returns its lowercase equivalent, if a lowercase representation exists in the current locale. Otherwise, it returns c. The tolower_l() function performs the same task, but uses the locale referred to by the locale handle locale.
OP's Problem:
toupper(key[i])
does not change the supplied argument. It returns the uppercase equivalent of the character, if available. You need to make use of it.
Change this:
toupper(key[i);
to:
key[i] = toupper(key[i]);
You're also missing a pair of parentheses around the if statement.

Related

Why does this program return a "?" symbol in C

I'm following along with cs50x and in problem set 2. This is the idea I had for solving the Caesar problem. I'm yet to implement the key idea due to the fact that it won't print out the word. I'm new to arrays and have searched a bit about why this is occurring. I think that I'm overcomplicating the code and could just use the string given by the user instead of transferring it to a function but now that I've started the idea I want to know why it isn't working and if there is a way to make it work. When ran, the program should accept a command line of a single number, if it has no command line it should fail, if the number is negative it should fail, if it is not a number it should fail and if it has more than 1 argument it should fail. Thanks
#include <cs50.h>
#include <stdio.h>
#include <ctype.h>
#include <string.h>
#include <stdlib.h>
string cipher(string word, int key);
int main(int argc, string argv[])
{
// Checks whether the user inputted only 1 argument
if (argc == 2)
{
// Convert argv to an int
int key = atoi(argv[1]);
string plainText = get_string("plaintext: ");
// Use function to return the (soon to be encrypted) string
string cipherText = cipher(plainText, key);
// Print for how long the word is
int n = strlen(plainText);
for (int i = 0; i < n; i++)
{
// Print the char of the array based upon the iteration of the loop which runs for however long the word is
printf("%c", cipherText[i]);
}
printf("\n");
// If input is not a positive integer then it will fail
if (key < 1)
{
printf("Usage: ./caesar key\n");
}
}
else
{
// If user inputted too many or no inputs then it will fail
printf("Usage: ./caesar key\n");
}
return 0;
}
string cipher(string word, int key)
{
// Find the length of the word in order to set the size of the array
// This is so that both strings, the word inputted and the word to return are the same to add to
int n = strlen(word);
string cipherText[n];
// Loop through the input word
for (int i = 0; i < n; i++)
{
// If char[i] is a letter then copy that letter into ciphertext
if (isalpha(word[i]))
{
cipherText[i] =& word[i];
}
else
{
cipherText[i] =& word[i];
}
}
// Return the array which, for example the input word is nobody
// Return array[n, o, b, o, d, y]
return cipherText[0-n];
}
The issue is that you are attempting to copy the address of the "word" character array characters into the associated cipher text array element which will print out unknown characters (noted in the above comments).
// Loop through the input word
for (int i = 0; i < n; i++)
{
// If char[i] is a letter then copy that letter into ciphertext
if (isalpha(word[i]))
{
cipherText[i] = &word[i];
}
else
{
cipherText[i] = &word[i];
}
}
When I ran your program with the code like that, I indeed got a series of question marks.
#Una:~/C_Programs/Console/CypherCS50/bin/Release$ ./CypherCS50 12
plaintext: Hello
?????
I then revised it to perform a copy of character elements from "word" to "cipherText".
// Loop through the input word
for (int i = 0; i < n; i++)
{
// If char[i] is a letter then copy that letter into ciphertext
if (isalpha(word[i]))
{
cipherText[i] = word[i];
}
else
{
cipherText[i] = word[i];
}
}
Then, reran the program.
#Una:~/C_Programs/Console/CypherCS50/bin/Release$ ./CypherCS50 12
plaintext: Hello
Hello
Seeing that the same data came out, my guess is that you still need to work on the actual encryption bits. But, the crux of the issue was referencing the memory of the work array elements.
Give that a try.
This does not fix your OP issue, but addresses another issue and responds to our exchange in comments above. Here is a "skeleton" demonstrating how you might approach incrementally developing code for this task. The 'excessive' printf's serve to prove that things are proceeding as you want as the source code becomes more elaborate..
// Functions defined before use do not need to be prototyped
// do-nothing "skeleton" to be completed
string cipher(string word, int key)
{
printf( "In cipher with key %d and str '%s'\n", key, word ); // temp code confirmation
return word; // for now...
}
int main(int argc, string argv[])
{
if (argc != 2)
{
printf("Usage: ./caesar key\n");
return -1; // early termination
}
printf( "argv[1] = %s\n", argv[1] ); // prove functionality
int key = atoi(argv[1]);
printf( "key = %d\n", key ); // prove functionality
if (key <= 0)
{
printf("Key must be positive integer");
return -1; // early termination
}
string plainText = get_string("plaintext: ");
printf( "plain = %s\n", plainText ); // prove functionality
string cipherText = cipher(plainText, key);
printf( "cipher = %s\n", cipherText ); // prove functionality
return 0; // All done!
}

How to put a group of changes to an array and then print all of them as one in C

I'm solving a coding problem currently. I must encipher a code and change words into other encoded words. I know I can check and print each character. However, I want to try to save the result of the checking of each character to an array, and then later I want to just print it.
Below is the full code... I deeply appreciate it if anyone can help me answer my question, or if it is even possible.
//one command line argument with the type int
int main(int argc, string argv[])
{
//setting condition that: K = +; if more or less than one, immediate error message, return 1
//if not decimal return = usage ./caesar. key, return value 1 to main
if (argc != 2)
{
printf("Error 404 : \n");
return 1;
}
//main program
else if ( argc == 2 && isdigit(*argv[1]))
{
int k = atoi(argv[1]);
string pltext = get_string("plaintext: "); //getting input for the plain text
char cptext[]
for (int i = 0, n = strlen(pltext) ; i < n; i++) //turning pltext to integer
{
if (pltext[i] >= 'a' && pltext[i] <= 'z')
{
cptext[i] = ((pltext[i] - 'a' + k)%26)+'a'; //shifting the integer with k (lowercase)
}
else if (pltext[i] >= 'A' && pltext[i] <= 'Z')
{
cptext[i] = ((pltext[i] - 'A' + k)%26)+'A'; //shifting the integer with k (uppercase)
}
else
{
cptext[i] = pltext[i]; //other symbol stays
}
}
//print out result
string cptext = ("test");
printf("ciphertext: %s\n", cptext[]);
}
else
{
printf("Usage: ./caesar key\n");
return 1;
}
}
You should allocate an array according to the number of elements to be stored.
char cptext[]
should be
char cptext[strlen(pltext) + 1]; // +1 for terminating null-character
(variable-length array introduced in C99)
and
string cptext = ("test");
printf("ciphertext: %s\n", cptext[]);
should be
cptext[strlen(pltext)] = '\0'; // terminate the string
printf("ciphertext: %s\n", cptext); // remove extra []

Why am I getting errors?

I'm trying to create a program that accepts cmd line arguments to encipher a plaintext!
The program must accept one cmd line argument after its name when making it and this would be the key which by the plaintext (only) alphabetical characters are rotated by this key (e.g. it's number is added to the real alphabet ASCII number resulting in another alphabet to be printed!
it is supposed to print an error message when one argument is present (e.g. here:/make encipher)
instead of here:/make encipher 12 <-- 12 = key!
I am getting a segmentation fault when running the program without the key argument, why?
This is the full code. I'm posting it because I need to learn where is my fault's exact location
and why is it triggered?!
#include <cs50.h>
#include <stdio.h>
#include <stdlib.h> // To use atoi (converting a string to an int)
#include <ctype.h>
#include <string.h>
bool key_is_numb(string argv[]);
void encipher(string txt, int key);
int main(int argc, string argv[])
{
if (key_is_numb(argv) == false)
{
printf("Usage: ./caesar key\n");
return 1;
}
else
{
int key = atoi(argv[1]);
string plaintext;
if (argc == 2 && key > 0)
{
plaintext = get_string("plaintext: ");
encipher(plaintext, key); // A function that prints the ciphered text
return 0; // returns Zero as main return value which means "All good"!
}
else if (argc == 1 || argc > 2 || key <= 0)
{
printf("Usage: ./caesar key\n");
return 1;
}
} // End else.
} // End main()å func.
bool key_is_numb(string argv[])
{
int n = strlen(argv[1]);
for (int i = 0; i < n; i++) // checking element by element in the second string of the argv[] array of strings
{
if (isdigit(argv[1][i]) == 0) // if the entered string "key" contains chars other than digits.
{
return false; // break out of the if statement & the entire function key_is_numb()
// and return false as soon as a letter is encountered.
}
else
{
continue; // go up & start the next iteration for the for loop.
}
// if only digits encountered then this for loop will come to an end and exist from here.
} // End for loop
return true; // function exits and return boolean true from here.
} // End key_is_numb() func.
void encipher(string txt, int key)
{
printf("ciphertext: ");
for (int i = 0, n = strlen(txt); i <= n; i++) // strlen counts the number of elements in a string excluding '\0'
{
char c = txt[i];
if (isalpha(c))
{
if (isupper(c))
{
char m = 'A'; // This is a modifyer character equals to 'A' = 65 so that it is indexed # ZERO!
printf("%c", (c - m + key) % 26 + m );
//c = ((((int)txt[i] - 65) + key) % 26) + 65; // char c = 65 <-- 65 is an ASCII code equals 'A'
}
else if (islower(c))
{
char m = 'a'; // This is a modifying character 'a' = 97
printf("%c", (c - m + key) % 26 + m );
}
}// End if(alpha).
else
{
printf("%c", c);
}
} // End for().
printf("\n");
} // End encipher() func.
int n = strlen(argv[1]);
in key_is_numb() and
int key = atoi(argv[1]);
in main().
If you didn't enter a key argument, argv[1] as equal as argv[argc] is a null pointer as stated in C17, §5.1.2.2.1/2.
Any attempt to access its data is undefined behavior and probably caused the segmentation fault.
Well you are assuming that argv[1] is defined in key_is_numb. However, in C and C++, the second parameter of the main function contains command line arguments. Which, in your case will be the name of the binary as the first element, then any other arguments. This is why when you are running the program without arguments, it will segfault, as there are no argument to put in argv, and no default value either.
You should always check the size of argv, by using the number stored in argc, before trying to read anything in argv.
Your segmentation fault comes from this line int n = strlen(argv[1]);, but I'd highly suggest you to learn to use debugger software like valgrind, which if the program has been compiled with debug flag will tell you the exact line.
Other debugger are really useful too, so you should learn to use them, as they usually report this kind of errors.
Your code asumes there is always an argv[1]. You should check argc which tells the number of arguments. For example:
int main(int argc, string argv[])
{
if (argc < 2) {
printf("Key required\n");
exit (1);
}

CS50 Caesar program is working but check50 says it isn't

I created this program but I'm getting errors on CS50 showing that I didn't do any of it correctly.
The requirements are as follows:
Implement your program in a file called caesar.c in a directory called caesar.
Your program must accept a single command-line argument, a non-negative integer. Let’s call it k for the sake of discussion.
If your program is executed without any command-line arguments or with more than one command-line argument, your program should print an error message of your choice (with printf) and return from main a value of 1 (which tends to signify an error) immediately.
If any of the characters of the command-line argument is not a decimal digit, your program should print the message Usage: ./caesar key and return from main a value of 1.
Do not assume that k will be less than or equal to 26. Your program should work for all non-negative integral values of k less than 2^31 - 26. In other words, you don’t need to worry if your program eventually breaks if the user chooses a value for k that’s too big or almost too big to fit in an int. (Recall that an int can overflow.) But, even if k is greater than 26, alphabetical characters in your program’s input should remain alphabetical characters in your program’s output. For instance, if k is 27,
A should not become [ even though [ is 27 positions away from A in ASCII, per http://www.asciichart.com/[asciichart.com]; A should become B, since B is 27 positions away from A, provided you wrap around from Z to A.
Your program must output plaintext: (without a newline) and then prompt the user for a string of plaintext (using get_string).
Your program must output ciphertext: (without a newline) followed by the plaintext’s corresponding ciphertext, with each alphabetical character in the plaintext “rotated” by k positions; non-alphabetical characters should be outputted unchanged.
Your program must preserve case: capitalized letters, though rotated, must remain capitalized letters; lowercase letters, though rotated, must remain lowercase letters.
After outputting ciphertext, you should print a newline. Your program should then exit by returning 0 from main.
My code:
#include <cs50.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
int main(int argc, string argv[])
{
//check if k inputed
if (argc != 2)
{
printf("Usage: ./caesar key\n");
return 1;
}
//value k is the number after ./caesar
int k = atoi(argv[1]) % 26;
int x = 0;
int s = strlen(argv[1]);
//check if k is a positive integer
if (k < 0)
{
printf("Usage: .caesar key\n");
return 1;
}
else
{
//check for arguments
for (int i = 0; i < s; i++)
{
if (isalpha (argv[1][i]))
{
continue;
}
else if (isalnum (argv[1][i]))
{
x++;
}
else
{
continue;
}
}
if (x != s)
{
printf("Usage: ./caesar key\n");
}
else if (x == s)
{
//get plaintext
string plain_text = get_string("plaintext: ");
printf("ciphertext: ");
for (int y = 0; y <= strlen(plain_text); y++)
{
//change letters
if (isalpha(plain_text[y]))
{
char p = plain_text[y];
int cipher_int = p + k;
if (isupper(p))
{
while(cipher_int >= 90)
{
cipher_int -= 26;
}
char cipher_text = cipher_int;
printf("%c", cipher_text);
}
if (islower(p))
{
while(cipher_int >= 122)
{
cipher_int -= 26;
}
char cipher_text = cipher_int;
printf("%c", cipher_text);
}
}
else
{
printf("%c", plain_text[y]);
}
}
printf("\n");
}
}
return 0;
}
It appears that your wrapping is not working correctly. I found that when I used 3 as the key and put "The quick fox jumps over the lazy brown dog." as the plain text, "brown" became "eur`q" when it should be "eurzq". I think you're using >= in your wrapping comparisons when you should use >.
Your check for digits is very cumbersome and does not cause the program to return 1 as required if the argument is incorrect.
Here is a simpler test:
//check for arguments
for (int i = 0; i < s; i++) {
if (!isdigit((unsigned char)argv[1][i])) {
printf("Usage: ./caesar key\n");
return 1;
}
}
Also note that you should stop the encoding loop when the index == the length of the string. therefore the operator should be <.
Another problem is the use of isalpha() and similar functions from <ctype.h> with char values. These functions are undefined for negative values (except EOF). Some platforms define char as signed by default, making isalpha(plaintext[y]) have undefined behavior if the user typed non ASCII text. Cast the argument as (unsigned char) to avoid this problem.
Furthermore, you should not use hardcoded ASCII values such as 90 and 122, use character constants such as 'a' and 'z' for better readability. Doing so would make another error in your encoding loop more obvious: while(cipher_int >= 90) should be if (cipher_int > 'A') and while(cipher_int >= 122) should be if(cipher_int > 'z').
Here is a modified version:
#include <cs50.h>
#include <stdio.h>
#include <stdlib.h>
#include <ctype.h>
int main(int argc, string argv[])
{
// check for a single command line argument
if (argc != 2) {
printf("Usage: ./caesar key\n");
return 1;
}
char *arg = argv[1];
if (*arg == '\0') {
printf("caesar: key cannot be an empty string\n");
return 1;
}
// check that the argument is a non negative number
for (size_t i = 0; arg[i]; i++) {
if (!isdigit((unsigned char)arg[i])) {
printf("Usage: ./caesar key\n");
return 1;
}
}
// value k is the shift number after ./caesar
int k = atoi(argv[1]) % 26;
// get plaintext
string plain_text = get_string("plaintext: ");
printf("ciphertext: ");
for (size_t i = 0; plain_text[i] != '\0'; i++) {
unsigned char c = plain_text[i];
// change letters
if (islower(c)) {
putchar('a' + ((c - 'a') + k) % 26);
} else
if (isupper(c)) {
putchar('A' + ((c - 'A') + k) % 26);
} else {
putchar(c);
}
}
printf("\n");
return 0;
}

All outputs end up the same in C

I'm trying to iterate over key which has been entered into the command line. During the iteration, I want to create 2 strings, upper and lower, which can be used later. The problem is that key, upper and lower are all ending up with the same outputs. Could someone help me figure out where I'm going wrong?
int main(int argc, char *argv[])
{
if (argc != 2)
{
printf("Usage: ./substitution key\n"); // if no key is entered on command line, end program and say key
return 0;
}
string key = argv[1]; // convert command to key for ease
string alpha = "abcdefghijklmnopqrstuvwxyz"; // lowercase alphabet
string cap_alpha ="ABCDEFGHIJKLMNOPQRSTUVWXYZ"; //uppercase alphabet
string lower = argv[1]; // gives string correct length to edit later
string upper = argv[1]; // ^ditto
if (strlen(key) != 26) // if key is not exactly 26 char, end program and say it must contain 26
{
printf("Key must contain 26 characters\n");
return 0;
}
for (int i = 0; i < strlen(key); i++) //iterates through key
{
if (key[i] < 'A' || (key[i] > 'Z' && key[i] < 'a') || key[i] > 'z') //if key includes non alphabet characters, ends command
{
printf("Key must only contain alphabetic characters.\n");
return 0;
}
else if (key[i] >= 'A' && key[i] <= 'Z')
{
lower[i] += 32; // if char in key is uppercase, convert to lowercase, add to string "lower"
upper[i] = key[i]; // if char in key is uppercase, keeps as uppercase
}
else
{
upper[i] = key[i] - 32; // if char in key is lowercase, changes to uppercase
lower[i] = key[i];
}
}
printf("key: %s\n", key); // test output
printf("lower: %s\n", lower); //test output
printf("upper: %s\n", upper); // test output
}
Assigning strings doesn't make copies, it just assigns a pointer to the same string. So key, upper, lower, and argv[1] are all the same string. When you make a change to one of them, it affects all of them.
You need to make copies first.
size_t len = strlen(key) + 1; // +1 for the null byte
char upper[len], lower[len];
strcpy(upper, key);
strcpy(lower, key);
BTW, C has functions isalpha(), isupper(), islower(), toupper(), and tolower(). You should use these instead of relying on the specifics of ASCII coding.

Resources