My program seems to be working fine and it does as intended, it takes command line arguments and "rotates" the inputted string from the prompt depending on the inputted command line argument. However, if I run my code without any arguments like: ./caesar it doesn't work, it says "segmentation fault (core dumped)" but if i run it like this ./caesar 1 or any other number, it works as intended.
#include <cs50.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <ctype.h>
int only_digits(string n);
char rotate(char i, int n);
int main(int argc, string argv[])
{
if(only_digits(argv[1]) && argc == 2) {
string plaintext = get_string("plaintext: ");
printf("ciphertext: ");
int k = atoi(argv[1]); // converts string n to k
for (int i = 0, l = strlen(plaintext); i < l; i++)
{
printf("%c", rotate(plaintext[i], k));
}
printf("\n");
}
else
{
printf("Usage: ./caesar key\n");
return 1;
}
}
int only_digits(string n) // function that returns 1 or 0 depending if given string is only digits
{
int state;
for(int i = 0, l = strlen(n); i < l; i++)
{
if (isdigit(n[i])) // checks if characters in string is a digit from 1 to 9
{
state = 1;
}
else
{
state = 0;
break;
}
}
return state;
}
char rotate(char c, int n)
{
char rotated_char = c;
if (isupper(c))
{
int a_index = c - 'A';
int c_cipher = (a_index + n) % 26;
rotated_char = c_cipher + 'A';
}
else if (islower(c))
{
int a_index = c - 'a';
int c_cipher = (a_index + n) % 26;
rotated_char = c_cipher + 'a';
}
return rotated_char;
}```
The quickest and easiest fix is to replace:
if(only_digits(argv[1]) && argc == 2)
With:
if(argc == 2 && only_digits(argv[1]))
Just swap the two sub-expressions around. When argc is 1 (ie, no arguments), argv[1] is NULL, and only_digits() cannot handle that - strlen(NULL) is incorrect.
However, if we do argc == 2 first, short-circuit evaluation rules say that since FALSE && anything is always FALSE, we don't need to bother evaluating only_digits(), so the code is never called and thus the crash is avoided.
You've got two good answers already.
My suggestion is to consider both the user and those programmers who will revise your code. Inform the user of the command line problem and don't 'hide' the 'fail' process at the bottom... If the processing is to stop, show that at the top (and you can save one level of indent, too!)
int main( int argc, string argv[] )
{
if( argc != 2 )
{
printf("Usage: ./caesar key [where key is all numeric]\n");
return 1;
}
if( !only_digits( argv[1] ) )
{
printf( "Key must be an integer value\n");
return 1;
}
int k = atoi( argv[1] ); // converts string n to k
string plaintext = get_string( "plaintext: " );
printf( "ciphertext: " );
for( int i = 0, l = strlen( plaintext ); i < l; i++ )
{
printf( "%c", rotate( plaintext[i], k ) );
}
printf("\n");
return 0;
}
Related
I am trying to print the output of the function "char rotate(char c, int n)" but it will print only the numbers and not the characters. any help is appreciate it.
#include <cs50.h>
#include <ctype.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
bool only_digits(string arg);
char rotate(char c, int n);
int main(int argc, string argv[])
{
// Make sure program was run with just one command-line argument
if (argc !=2)
{
printf("Usage: ./caesar key\n");
return 1;
};
// Make sure every character in argv[1] is a digit
if ( only_digits(argv[1])==0)
{
printf("Usage: ./caesar key\n");
return 1;
};
// Convert argv[1] from a `string` to an `int`
int key = atoi(argv[1]);
// Prompt user for plaintext//
string text = get_string("plaintext: \n");
// For each character in the plaintext:
printf("ciphertext: ");
for (int i = 0; i < strlen(text); i++)
{
char ch = (rotate(text[i], key));
printf("%c", ch);
};
}
bool only_digits(string arg)
{
// Rotate the character if it's a letter
for (int i = 0; i < strlen(arg); i++)
{
if (!isdigit(arg[i]))
{
return false;
}
}
return true;
}
char rotate(char c, int n){
if (isupper(c))
{
c -=65;
c = (c + n) % 26;
c = c + '0';
return c;
}
else if (islower(c))
{
c -=97;
c = (c + n) % 26;
c = c + '0';
return c;
}
else
return c;
}
I tried different ways of casting the integers into characters and experimenting with a debugger and using printf statement to figure out what's causing this to no avail.
Edit1:
in my original post I deleted bunch of my code thinking it's not relevant to my issue but some of the questions asked about those parts, so hopefully now it's more clear.
when I type abc it print no thing at all, just "plaintext: "
OP eventually determined a major issue.
Code was substring a '0' when an 'A' or 'a' should have been subtracted.
To handle other issues:
is...(ch) has trouble when ch < 0
Best to use unsigned char.
Avoid magic numbers like 65
Avoid negatives and overflow errors
c + n may overflow
The sum may be negative. So let us work with certain positive numbers.
Unneeded else
// In main(), bring `key` into the range [0...25];
int key = atoi(argv[1]);
key &= 26; // key now in the [-25 ... 25] range
if (key < 0) key += 26;
// n is in the 0...25 range.
static char rotate(char c, int n) {
unsigned char uch = (unsigned char) c;
if (isupper(uch)) {
uch = (uch - 'A' + n) % 26;
return uch + 'a';
}
if (islower(uch)) {
uch = (uch - 'a' + n) % 26;
return uch + 'a';
}
return c;
}
Code compiles ok. I tried to use a debugger but couldn't figure out the problem.
Each iteration "c" is being filled, but it seems like the way I try to assign this variable "c", into the array nothing happens, so that the end print statement ends up "ciphertext: EMPTY NOTHING"
Any help or ideas very welcomed. I'm taking courses and trying to solve a problem set. I'm only begginer, pls do not judje hard :)
#include <stdio.h>
#include <cs50.h>
#include <string.h>
#include <math.h>
#include <stdlib.h>
#include <ctype.h>
int main (int argc, string argv[])
{
if(argc < 2 || argc<2 )
{
printf("Usage: ./caesar key \n");
return 1;
}
else if(argc == 2)
{
for (int i = 0; i < strlen(argv[1]); i++)
{
if(!isdigit(argv[1][i]))
{
printf("Usage: ./caesar key \n");
return 1;
}
}
}
int key = atoi(argv[1]);
string initial_text = get_string("plaintext: ");
int cipher[strlen(initial_text)];
int i = 0;
int n = strlen(initial_text);
for (i = 0; i < n; i++)
{
int c = 0;
if (isupper(initial_text[i]))
{
c = (((int)initial_text[i] - 65 + key) % 26) + 65;
cipher[i] += c;
}
else if (islower(initial_text[i]))
{
c = (((int)initial_text[i] - 97 + key) % 26) + 97;
cipher[i] += c;
}
else
{
c=initial_text[i];
cipher[i] += c;
}
}
//////////////////
printf("ciphertext: %c\n", (char)cipher);
}
There are a number of issues:
Using int for cipher when you [probably] want char.
Not defining enough space in cipher for the trailing 0x00 [EOS] string terminator
Not setting the EOS at the end.
Using %c instead of %s in your printf.
if ((argc < 2) || (argc < 2)) is wrong and can be replaced with if (argc != 2).
Because that if does return, the subsequent else if check is no longer needed.
"hardwiring" decimal values (e.g. 65 for 'A').
Using strlen in the condition expression in a for loop. This increases the running time of the loop from O(n) to O(n^2).
This can be replaced with (e.g.) comparing the array element against 0x00.
Using strtol instead of atoi and checking the end can do decode and validation in a single operation.
Don't use string when defining argv -- use char **argv.
Your main lacks a return 0; at the end.
There's a lot of replicated code that can be simplified.
Here's the refactored code:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
// I don't have cs50 handy, so a quick hack ...
#define string char *
string
get_string(const char *prompt)
{
char buf[1000];
char *cp;
printf("%s",prompt);
fflush(stdout);
do {
cp = fgets(buf,sizeof(buf),stdin);
if (cp == NULL)
break;
// strip newline
buf[strcspn(buf,"\n")] = 0;
// IIRC, [real] get_string does heap allocation (vs. static buffer) but
// for demo purposes here, doesn't really matter
cp = strdup(buf);
} while (0);
return cp;
}
int
main(int argc, char **argv)
{
// check if there is exactly 2 arguments passing, otherwise prompt for a
// valid key(+return 1, to signal mistake)
if (argc != 2) {
printf("Usage: ./caesar key \n");
return 1;
}
// otherwise check if the key is only a digit and prompt for a valid
// key(+return 1, to signal mistake)
// loop through each character in the second argument and validate
char *arg = argv[1];
// convert a string to int variable to be used in transformation of letters
int key = strtol(arg,&arg,10);
if (*arg != 0) {
printf("Usage: ./caesar key \n");
return 1;
}
// prompt for plaintext
string initial_text = get_string("plaintext: ");
// create an array of int to store encrypted letters to then be converted
// in print by (char)
// NOTE: we need one extra to make room for the trailing EOS
char cipher[strlen(initial_text) + 1];
const char *src = initial_text;
char *dst = cipher;
for (int chr = *src++; chr != 0; chr = *src++, ++dst) {
if (isupper((unsigned char) chr)) {
*dst = (((chr - 'A') + key) % 26) + 'A';
continue;
}
if (islower((unsigned char) chr)) {
*dst = (((chr - 'a') + key) % 26) + 'a';
continue;
}
// for all none alphabetic symbols leave them as they are
*dst = chr;
}
// set the trailing EOS
*dst = 0;
// ////////////////
printf("ciphertext: %s\n", cipher);
return 0;
}
You could write for example
if ( argc < 2 || argc<2 || argc < 2 )
instead of
if ( argc < 2 || argc<2 )
and this will be correct but does not make a sense.
Just write
if ( argc != 2 )
{
puts( "Usage: ./caesar key" );
return 1;
}
This else statement
else if(argc == 2)
is redundant. Remove it.
Instead of this for loop
for (int i = 0; i < strlen(argv[1]); i++)
{
if(!isdigit(argv[1][i]))
{
printf("Usage: ./caesar key \n");
return 1;
}
}
it is simpler to write
char *p;
unsigned long ley = strtoul( argv[1], &p, 10 );
if ( *p || key == ULLONG_MAX )
{
puts( "Usage: ./caesar key" );
return 1;
}
There is no need to define a variable length array. You could change the original string pointed to by the pointer initial_text.
Introducing the variable n is redundant
int n = strlen(initial_text);
Instead of the loop
for (i = 0; i < n; i++)
you could use this loop
for ( size_t i = 0; initial_text[i] != '\0'; i++ )
Instead of these if statements
if (isupper(initial_text[i]))
and
else if (islower(initial_text[i]))
it will be more safer to write
if ( isupper( ( unsigned char )initial_text[i]))
and
else if ( islower( ( unsigned char )initial_text[i] ) )
Also using magic numbers like 65 makes the code unreadable. For example you could
write
c = ( initial_text[i] - 'A' + key ) % ( 'Z' - 'A' + 1 ) + 'A';
and
c = ( initial_text[i] - 'a' + key ) % ( 'z' - 'a' + 1) + 'a';
and instead of the compound assignment operator
cipher[i] += c;
you need to write
initial_text[i] = c;
Also in the call of printf you are using incorrect conversion specifier %c and casting
printf( "ciphertext: %c\n", (char)cipher);
As the string should be updated in place then the call of printf will look like
printf( "ciphertext: %s\n", initial_text );
I'm a newbie, so apologies if I don't explain myself well. If it helps, I'm doing this for the Caesar problem set as part of the Harvard CS50x OpenCourseWare.
I'm trying to convert user generated plain text to cipher text using a simple key. To accomplish this I'm attempting to use a wraparound counting formula in my last function. However, sometimes I get blanks that print out instead of the new characters... Help!
EDIT: I'm using a key of 5 and the plaintext "Helloz!" to test. Expect to see Mjqqte!
instead am seeing blank spaces.
#include <stdio.h>
#include <cs50.h>
#include <string.h>
#include <math.h>
#include <ctype.h>
#include <stdlib.h>
int convert(string n);
string k, text;
char text;
int r, c, t,x;
bool validate(string n);
//int encrypted(string n);
int main(int argc, string argv[])
{
//accept single command-line argument, non negative integer, k with appropriate error
k = argv[1];
if (argc > 1 && argc <= 2)
{
//printf("Success\n%s\n", argv[1]);
// print individual characters of argv[i]
validate(k);
}
else //if wrong input then print error message and main should return 1
{
printf("Usage: ./caesar key\n");
return 1;
}
text = get_string("plaintext:");
t = atoi(k);
printf("%i\n", t);
convert (text);
printf("\n");
}
//output "ciphertext:" without a newline, with the characters roated by k positions
//after output, print a newline and exit by returning 0 from main
bool validate(string n)
{
for (int i = 0; k[i] != '\0'; i++)
{
if (48 <= k[i] && k[i] <= 57)
{
//printf("%c\n", k[i]);
}
else
{
printf("./caesar key\n");
return 1;
// save for later: printf("%s \n", k);
}
}
return r;
}
int convert(string n)
{
//if fits within a range, Reads individual characters
for (int i = 0; i < text[i]; i++)
{
if (isalpha(text[i]))
{
x = text[i];
//printf("%i\n", x);
c = (x+t) % 26;
// printf("%i\n",c);
printf("%c", c);
}
else
{
printf("%i", text[i]);
}
}
return 0;
}
Here's an implementation that could work for you:
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <ctype.h>
void convert(char *text, unsigned char k) {
for (unsigned int i = 0; i < strlen(text); i++) {
if (isalpha(text[i])) {
// Contains the 3 leftmost bits, containing the uppercase/lowercase part.
char c = (text[i] / 32) * 32;
// Perform the shifting with modulo on the alphabetic index of the letter.
text[i] = c + ((text[i] % 32) + k) % 26;
}
}
}
int main(int argc, char *argv[]) {
unsigned char k = strtol(argv[1], NULL, 10);
char text[64];
printf("Using key %d.\n", (int) k);
printf("Plaintext: ");
fgets(text, 64, stdin);
// Remove newline.
text[strlen(text) - 1] = 0;
convert(text, k);
printf("Ciphertext: %s.\n", text);
return 0;
}
Test run:
>>> cipher 4
Using key 4.
Plaintext: Test mE Right Away!!1
Ciphertext: Xiwx qI Vmklx Eaec!!1.
I was doing the cs50 pset 2 - substitution, where we have to encrypt the plaintext using the key given by the user in the command line, but the following code isn't prompting for an input. What am I doing wrong? Any help would be greatly appreciated!
#include <stdio.h>
#include <cs50.h>
#include <ctype.h>
#include <string.h>
#include <math.h>
int check(int c, string key);
int main(int argc, string argv[])
{
int checkR = check(argc, argv[1]);
if(checkR == 1)
{
return 1;
}
else
{
string key = argv[1];
string ptext = get_string("plaintext: ");
int len = strlen(ptext);
char ctext[len];
for (int i = 0; i < len; i++)
{
if(isupper(ptext[i]))
ctext[i] = toupper(key[(int)ptext[i] - 65]);
else if(islower(ptext[i]))
ctext[i] = tolower(key[(int)ptext[i] - 97]);
else
ctext[i] = ptext[i];
}
printf("ciphertext: %s\n", ctext);
return 0;
}
}
int check(int c, string key)
{
int keyL = strlen(key);
if(c != 2)
return 1;
else if(keyL != 26)
return 1;
for(int i = 0; i < keyL - 1; i++)
{
for(int j = i; j < keyL; j++)
{
if(key[i] == key[j])
return 1;
}
}
return 0;
}
Answer for the question
In the check function, you initialized the inner loop as int j = i.
Therefore, in the first iteration, key[i] == key[j] will be always true.
Then, 1 is returned from check and it prevents main function from printing the prompt.
The initialization should be int j = i + 1.
Other mistakes
Firstly, it is bad to do int keyL = strlen(key); before checking c because key (argv[1]) may be NULL when c (argc) is less than 2.
Secondly, printf("ciphertext: %s\n", ctext); in this code will invoke undefined behavior because what is stored in ctext is not NUL-terminated.
char ctext[len]; should be char ctext[len+1]; and ctext[len]='\0'; should be added before printf("ciphertext: %s\n", ctext);.
I am new here and working on the second homework Caesar of cs50, it seems most of my review is correct except the last one -- I cannot handle the situation of lacking argv[1], which means if I only type ./caesar, it will return segmentation fault. I am wondering why this code if (argc != 2) cannot return 0 when argc == 1, however it works when argc > 1, I find that is weird. Can anyone help me?? Thanks in advance!
# include <stdio.h>
# include <cs50.h>
# include <string.h>
# include <ctype.h>
# include <math.h>
# include <stdlib.h>
int check_the_key(int argc, string y);
int main(int argc, string argv[])
{
string x = argv[1];
int y = argc;
int k = check_the_key(y, x);
if (k == 0)
{
printf("ERROR!!!!!\n");
return 1;
}
else
{
// printf("The key is %i\n", k);
string text = get_string("Input your text:");
int i;
int n;
printf("ciphertext: ");
for (i = 0, n = strlen(text); i < n; i++)
{
if (islower(text[i]))
{
printf("%c", (text[i] - 97 + k) % 26 + 97 );
}
else if (isupper(text[i]))
{
printf("%c", (text[i] - 65 + k) % 26 + 65);
}
else
{
printf("%c", text[i]);
}
}
printf("\n");
return 0;
}
}
int check_the_key(int argc, string y)
{
int number = argc;
string key = y;
int numberkey = atoi(key);
if (argc != 2)
{
return 0;
}
else
{
if (numberkey > 0)
{
return numberkey;
}
else
{
return 0;
}
}
}
I know what is going on! Because I need to pass some value into atoi(), if I only call ./caesar, there is no value I can pass into atoi(), so it causes segmentation fault. Which means I need to change code order slightly, put int numberkey = atoi(key); inside the else loop. So the code will run if (argc != 2) first, if no, then go to the next step! Here is the code after change.
int check_the_key(int argc, string y)
{
int number = argc;
string key = y;
if (argc != 2)
{
return 0;
}
else
{
int numberkey = atoi(key);
if (numberkey > 0)
{
return numberkey;
}
else
{
return 0;
}
}
}