I try to solve the problem in pset2 of the CS50 lecture and when I run that code:
# include <stdio.h>
# include <string.h>
# include <cs50.h>
# include <ctype.h>
int main(int argc, string argv[])
{
// check if key is exists
if (argc != 2)
{
printf("%s", "usage: ./substitution key\n");
return 1;
}
else
{
// key
string ciphertext = argv[1]; // "VCHRPZGJNTLSKFBDQWAXEUYMOI"
int cipher_length = strlen(ciphertext);
string msg_error = "\0";
int x = 0;
if (cipher_length == 26)
{
int msg_num = 0;
for (int i = 0; i < strlen(ciphertext); i++)
{
if (isdigit(ciphertext[i]))
{
msg_num = 0; // false
}
else
{
msg_num = 1; // true
}
}
if (msg_num == 1)
{
string plaintext = get_string("plaintext: ");
int n = strlen(plaintext);
int a_letter = 'a';
char plaintext_cp[n]; // copy from the original text
char text[n];
strcpy(plaintext_cp, plaintext);
// to read the chars into plaintext
for (int i = 0; i < n; i++)
{
char lower_char = tolower(plaintext_cp[i]); // e.g -> d
if (lower_char >= 'a' && lower_char <= 'z')
{
int covert_to_ascii = lower_char; // 101
int index = covert_to_ascii - a_letter; // 101 - 98 = 4
if (islower(plaintext[i]))
{
text[i] = tolower(ciphertext[index]);
}
else
{
text[i] = toupper(ciphertext[index]);
}
}
else
{
text[i] = lower_char;
}
}
printf("ciphertext: %s\n", text);
return 0;
}
else
{
printf("%s\n", "key must only contain alphabetic characters.");
return 0;
}
}
else
{
printf("%s\n", "Key must contain 26 characters.");
return 1;
}
}
}
I get the output but also I get random output like this screen-shoot
I tried to check the condition if ciphertext[i] is '\0' but I get the same issue. can anyone explain what is going on and How I can solve this problem?
Looking at the screenshot you have provided, I do not think it is unintended output. The output comes out as should be, but, either while you were testing and CTRl+V'd into console, you did it too fast for the IDE and the string output followed behind it before going to a new line for a new input command; OR, it was just an accidental push onto the key and you haven't noticed. Otherwise it would have output that same (or if it is a flaw) another character every time you ran the command, but it seems to have only happened on the last command input.
(I have done this several times to myself when rushing or not paying attention)
The "printf" command under (// check if key exists) also has a %s (string pointer) but no string in the printf command, and isn't needed.
The last two printf's at the end of the file seem to have similar issues. The %s is not needed unless you have a string variable you plan on calling.
Related
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!
}
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 []
I don't know where did I do wrong here my first time trying to use
int main(int argc, argv[])
I just don't know if I'm using it correctly.
Here's some code:
The code compiles properly and does give me the result correctly as well
encrypts "BaRFoo" as "FeVJss" using 4 as key
Log
running ./caesar 4...
sending input BaRFoo...
checking for output "ciphertext: FeVJss\n"...
BUT here is my problem: when I type in 2r or 4w or any non numeric key, I get this messageļ¼
:( handles non-numeric key
timed out while waiting for program to exit
Can anyone tell me how to add another loop for this function to get the numerical key to print as -Usage: ./caesar key . please ? Thanks very much an appreciate your help and useful advice.
#include <string.h>
#include <cs50.h>
#include <stdio.h>
#include <ctype.h>
#include <stdlib.h>
int main(int argc, string argv[])
{
// Variable declarations
bool keySuccessful = false;
int key = 0;
int input_length = 0;
string text = "" ;
// The number of command line args submitted was incorrect.
do
{
if(argc != 2)
{
printf("Usage: ./caesar key .\n");
return 1;
}
else
{
// Convert ASCII char to a alphabate char
// Access individual char in the string plain
// get the key val and converted to integer
key = atoi(argv[1]);
keySuccessful = true;
printf("%s",argv[1]);
}
}
while(!keySuccessful);
// get user input
text = get_string("%s",text);
printf("ciphertext: ");
input_length = strlen(text);
for (int i=0; i<input_length; i++)
{
// Checking if is the lowercase a=97 to z=112
// Print out lower case with ky a=65 to z=90
if(isupper(text[i]))
{
printf("%c", (((text[i] - 65) + key) % 26) + 65);
}
else if(islower(text[i]))
{
printf("%c", (((text[i] - 97) + key) % 26) + 97);
}
else
{
printf("%c", text[i]);
}
}
printf("\n");
return 0;
}
If I understood your question correctly, you want to make sure the first argument is a number, and stop execution otherwise.
You could easily do that looping over the argument, and checking the output of isdigit() from ctype.h for each character, like this:
int argumentIsNumber = 1;
for (int i = 0; i < strlen(argv[1]); i++) {
if (isdigit(argv[1][i]) == 0) {
argumentIsNumber = 0;
}
}
if (argumentIsNumber == 0) {
// Tell the user the introduced key is wrong
}
As you see in this example, you could set a flag to 1, and change it to 0 whenever a non-digit is found. Then, when the loop ends, you only have the flag to 1 if all the characters were digits.
Hope it helps!
you should make code if user prompt somethink like
$ ./caesar 20x
you shoud print
Usage: ./caesar key
for (int i = 0 , n = strlen(argv[1]); i < n; i++)
{
if (isalpha(argv[1][i]))
{
printf("Usage: ./caesar key\n");
return 1;
}
}
I've been trying different solutions but am not sure where to look for the solution.
I prompt the user for "plaintext" and whatever their input is, the chars in their input need to be rotated alphabetically by a number (aka the key) which they provide.
For example: plaintext: HELLO would spit out ciphertext: IFMMP if the key were 1.
Assuming the key will always be a number, here is what my code looks like which attempts to rotate each char by Key: 1. I'm a real noob so please break it down is possible.
{
string s = get_string("plaintext: ");
printf("ciphertext: %s\n", s + 1);
}
The remaining code (which includes identifying and filtering out the key is:
#include <cs50.h>
#include <stdio.h>
#include <string.h>
#include <ctype.h>
#include <stdlib.h>
int main(int argc, string argv[])
{
int counter = 0;
if (argc == 2)
{
for(int k = 0; k <strlen(argv[1]); k++)
{
if (isdigit(argv[1][k]))
{
counter++;
}
}
if (strlen(argv[1]) == counter)
{
string s = get_string("plaintext: ");
if(s)
{
printf("ciphertext: %s\n", s + 1);
free(s);
}
}
else
{
printf("Usage: ./caesar key\n");
}
}
else
{
printf("Usage: ./caesar key\n");
}
}
Any assistance would be appreciated.
I do not have cs50, so cannot test everything in your code, but addressing your primary question: How Do I Rotate A Char Alphabetically By Integer X, the code section you have identified as where the rotation occurs:
if (strlen(argv[1]) == counter)
{
string s = get_string("plaintext: ");
printf("ciphertext: %s\n", s + 1);
}
But this section doesn't really rotate the text. It rather attempts to print the string obtained from the call to get_string after adding 1 to it. First, this is not modifying s at all. Second, s + 1 is not a legal operation. To rotate s, additional code is needed between those two lines.
If I understand get_string(), it is essentially a combination of printf(), fgets(,,stdin) and strdup().
So after the call to that function you are left with s which will be populated with whatever the user typed into stdin. For illustration, let
say s contains "what the user typed". How that string is represented in memory, including the NULL terminator, can be depicted as follows:
|w|h|a|t| |t|h|e| |u|s|e|r| |t|y|p|e|d|0|
To rotate this by X, as you have stated, each character needs to be modified by adding the value X, in this case 1
Showing ASCII equivalent values:
|119|104|97|116|32|116|104|101|32|117|115|101|114|32|116|121|112|101|100|0|
The rotated string then would be each value + 1:
For the general case, assume rotation value is read in from command line argument and placed into int n = 0;
earlier in code:
if(isdigit(argv[2][0]))
{
n = atoi(argv[2])
}
The rotation: (the following is complete. It may need a little debugging around the corner case [where values wrap around the end of ASCII values when added to n.] Lines are commented indicating where I used ANSI C instead of CS50. [I do not have CS50])
int main(int argc, char *argv[])//note: command line requires a numeric
// argument be used: prog.exe 12 (for n == 12)
{
//char s[80];//used to test (I do not have cs50)
char *rotated = NULL;
int i;
if(argc != 2)
{
printf("Usage: prog.exe <positive int value.>\nExiting\n");
return 0;
}
if(isdigit(argv[1][0]) == 0)
{
printf("Usage: prog.exe <positive int value.>\nExiting\n");
return 0;
}
int n = atoi(argv[1]);
string s = get_string("plaintext: ");
// printf("Enter text to be rotated\n");//used to test (I do not have cs50)
// scanf("%[^\n]", s);
// string rotated = strdup(s); //preserve s by manipulating identical string
rotated = StrDup(s);
if(rotated)
{ //advance value by n
int origVal = 0;
int len = strlen(rotated);
for(i=0;i<len;i++)
{
origVal = rotated[i];
if((rotated[i] + n) <= 127)
{
rotated[i] += n;
}
else
{
rotated[i] = abs(127 - (origVal + n));
if(rotated[i] == 0) rotated[i] += 23; //skip to printable characters (http://facweb.cs.depaul.edu/sjost/it212/documents/ascii-pr.htm)
//if(rotated[i] == 0) rotated[i] += 1; //skip only ASCII NULL value
}
}
rotated[i] = 0; //adding NULL to end of string
printf("ciphertext: %s\n", rotated);
free(rotated);
}
return 0;
}
You can achieve this with typecasting:
char input = 'f';
char output = (char)((int)input + 1);
You should also handle edge cases, i.e. when the character value is near the end of the alphabet.
I'm currently being a tutor for a student in C. For his classes, the university has installed a server running Mooshak (software capable of receiving code and test it).
We have developed code, compiled it and tested it locally before sending to the server and everything went fine. However, when we tried to send it to the server, the server stated "Memory Limit Exceeded".
The code looked as follows:
#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#include <limits.h>
#include <string.h>
#define LIMITE_CARACTERES 1000
#define LIMITE_GENES 100000
char genes[LIMITE_GENES][LIMITE_CARACTERES];
char* copiar_por_espaco(char* string, char* dest)
{
for(int i = 0; i < strlen(string); i++)
{
if(' ' == string[i])
{
strncpy(dest, string, i);
dest[i] ='\0';
if( i + 1 >= strlen(string))
return NULL;
else
return &string[i+1];
}
}
if(strlen(string) == 0)
{
return NULL;
}
else
{
strcpy(dest, string);
return NULL;
}
}
void genes_f()
{
char s[LIMITE_CARACTERES];
int numero_genes = 0;
while(scanf("%s", s) != EOF)
{
char *auxiliar = s;
while(auxiliar != NULL && strlen(auxiliar) != 0)
{
auxiliar = copiar_por_espaco(auxiliar, genes[numero_genes]);
numero_genes++;
}
}
if(numero_genes <= 20)
{
for(int i = 0; i < numero_genes; i++)
{
printf("%s\n", genes[i]);
}
}
else
{
for(int i = 0; i < 10; i++)
{
printf("%s\n", genes[i]);
}
for(int i = numero_genes - 10; i < numero_genes;i++)
{
printf("%s\n", genes[i]);
}
}
}
int main()
{
genes_f();
return 0;
}
Please note that the values LIMITE_CARACTERES and LIMITE_GENES are an assignment requirement (they haven't been told about memory allocation yet). The above code gives the "Memory Limit Exceeded", but if I split the first four into two lines, the server does not throw that error and accepts our solution:
char* copiar_por_espaco(char* string, char* dest)
{
int len = strlen(string); // This line was taken out from the for
for(int i = 0; i < len; i++) // Now we used the variable instead
{
if(' ' == string[i])
{
strncpy(dest, string, i);
dest[i] ='\0';
if( i + 1 >= strlen(string))
return NULL;
else
return &string[i+1];
}
}
if(strlen(string) == 0)
{
return NULL;
}
else
{
strcpy(dest, string);
return NULL;
}
}
I have no idea why. Is there an explanation for this?
The input will several lines with words (blank lines should be skipped), separated by a space. The program should separate and take each word:
Input
A BDD TES QURJ
test dog cat heart
cow
bird tree
Output
A
BDD
TES
QURJ
test
dog
cat
heart
cow
bird
tree
You forgot to include an extra byte for null terminators in your array. If LIMITE_CARACTERES is the maximum length of a string provided as input, then you need an array of size LIMITE_CARACTERES + 1 in which to store it. So you need to change this line
char genes[LIMITE_GENES][LIMITE_CARACTERES];
to
char genes[LIMITE_GENES][LIMITE_CARACTERES + 1];
Since you are a tutor, I give feedback so you can properly teach your student (so this is not an answer to your problem).
copiar_por_espaco
for(int i = 0; i < strlen(string); i++)
Repeatedly calling strlen on a variable that does not change in the loop is a waste of CPU cycles. Indeed, you should calculate the length before the loop and use it in the loop. That also holds for if( i + 1 >= strlen(string))
if(' ' == string[i])...
Note that it is guaranteed the string does not hold spaces because it was read with scanf. As a consequence, the function will always return NULL.
if(strlen(string) == 0) return NULL;
You test this after the loop but logic dictates you do this before any processing and it could be shortened to if (!*string) return NULL; This would also make the code more beautiful as the else part is not needed (it is not needed anyway).
genes_f
while(scanf("%s", s) != EOF)
A scanf-guru might help here but I believe there must be a space in the format specifier so it will skip leading spaces, " %s". I believe your way will read only one string and then will loop indefinitely returning zero on each scanf call. You should test the result of scanf for the number of format specifiers successfully converted and not for EOF. So check for 1.
if(numero_genes <= 20)
Your printing is funny. It all can be as one loop:
for(int i = numero_genes; i < numero_genes; i++)
printf("%s\n", genes[i]);
You have to do bounds checks on your number of genes:
numero_genes<LIMITE_GENES