Simple Caesar shift in C - c

I'm trying to create a simple Caesar shift program in C but I can't seem to figure it out. The program keeps crashing. Any help would be greatly appreciated.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main(int arc, const char* argv[])
{
int shift = atoi(argv[1]);
char message[256];
strcpy(message, argv[2]);
int i;
for(i = 0; i < strlen(message); i++) {
printf("%c", message[i] + shift);
}
putchar('\n');
return 0;
}

You're not correctly implementing the Caesar Cipher. Your code includes the following line, which is wrong:
printf("%c", message[i] + shift);
To do this correctly, you'll want to convert that to a function:
printf("%c", encrypt(message[i], shift));
And let's implement the function:
char encrypt(char input, int shift) {
if (input >= 'a' && input <= 'z')
return ((input - 'a' + shift) % 26) + 'a';
if (input >= 'A' && input <= 'Z')
return ((input - 'A' + shift) % 26) + 'A';
return input;
}
And just to explain what the math is doing in that function:
input - 'a' tells us what position in the alphabet the input is (assuming input is a lowercase letter). So if the input is 'c', then we will get a 2 back. If the input is 'z', we get 25.
input - 'a' + shift gets us the new position of the character that we are using to do the cipher. Note that this could be a larger number than the alphabet (26 characters).
So to solve that problem, we use modular arithmetic to bound that number between [0 - 25].
Then adding 'a' to that character gets us the actual character we want to print.
Note that this only works because the character codes for a to z and A to Z are consecutive.

There are three problems with your program.
The program reads from argv[1] and argv[2], but it assumes that the program receives at least 2 arguments. If it doesn't receive this many, then it can crash or do arbitrary things. You should explicitly check that the program receives at least (or exactly) 2 command-line arguments:
if (argc != 3) {
fprintf(stdout, "Not enough arguments\n");
exit(1);
}
Note: Rename arc to argc, and there is an extra implicit argument for the program name, which is why we check for 3.)
The program copies argv[2] into a fixed-size buffer. If the message is longer than 255 characters (plus null terminator), then it can overwrite memory and cause arbitrary things to happen. In the current situation, you can process the characters of argv[2] directly without copying it to a temporary variable:
for (i = 0; argv[2][i] != '\0' ; i++) {
printf("%c", encrypt(argv[2][i], shift));
}
The Caesar shift needs to wrap around after z or Z. See sharth's answer.

Related

Counting how many letters were repeated in the entered word

Your program should read a word from the input and then sort the letters of the word alphabetically (by their ASCII codes). Next, your program should iterate through the letters of the word and compare each letter with the one following it. If these equal each other, you increase a counter by 1, making sure to then skip ahead far enough so that letters that occur more than twice are not counted again. You may assume that the word you read from the input has no more than 50 letters, and that the word is all lowercase.
I wrote a program and get these results:
apple gives me 1
erroneousnesses gives 5,
but taylor should give me 0 however it gives 1.
How can I make yup, taylor and other words with non-repeating alphabets give me 0?
Here is my code:
#include <stdio.h>
int main(void) {
char string[51];
int c = 0, count[26] = { 0 }, x = 0;
scanf("%s", string);
while (string[c] != '\0') {
if (string[c] >= 'a' && string[c] <= 'z') {
x = string[c] - 'a';
count[x]++;
}
c++;
}
printf("%d", count[x]);
return 0;
}
There are multiple problems in your code:
scanf("%s", string); may cause a buffer overflow if the input is longer than 50 bytes.
Furthermore, you do not test the return value to check for invalid or missing input.
counting the number of occurrences of each letter is an alternative approach to the stated problem, and OK if you are only graded based on the program output. However be aware that the program does not implement what the assignment explicitly mandates and the value you output is incorrect anyway.
the output value should be the number of counts that are >= 2. Printing the value of count[x] is meaningless.
Here is a modified version:
#include <stdio.h>
int main() {
char str[51];
int dup = 0;
int count['z' - 'a' + 1] = { 0 };
if (scanf(" %50[a-z]", str) == 1) {
for (int i = 0; str[i] != '\0'; i++) {
if (++count[str[i] - 'a'] == 2)
dup++;
}
printf("%d\n", dup);
}
return 0;
}
to answer your specifc question
printf("%d", count[x]);
prints the count of number of appearances of the last letter.
taylor => 1 (r occurs 1 time)
apple => 1 (e once)
erroneousnesses => 5 (s count)
you need to loop over count array adding all counts > 1

CS50x Caesar - Segmentation fault when inserting isalpha(argv[1])

For the CS50x Problem Caesar I have created a program that encrypts messages using Caesar’s cipher.
For this, the program must recognize that a command-line argument is only a number. So no two or more numbers, no number below zero, and no text.
But as soon as I add the check if it is a text with || isalpha(argv[1]), the program does not work anymore.
The terminal prints the following when I try to run the program:
Segmentation fault
Can anyone tell me what is the problem with the code
#include <cs50.h>
#include <stdio.h>
#include <stdlib.h>
#include <ctype.h>
#include <string.h>
int main(int argc, string argv[])
{
int kkey = 0;
// Check if correct command-line arguments
if (argc != 2 || atoi(argv[1]) < 0 || isalpha(argv[1])) //segfault here
{
printf("./caesar key\n");
return 1;
}
else
{
kkey = atoi(argv[1]);
}
// Ask for Plaintext to encrypt
string plaintext = get_string("plaintext: ");
for (int i = 0, n = strlen(plaintext); i < n; i++)
{
if (isalpha(plaintext[i]) && islower(plaintext[i]))
{
plaintext[i] = (plaintext[i] - 'a' + kkey) % 26 + 97;
}
else if (isalpha(plaintext[i]) && isupper(plaintext[i]))
{
plaintext[i] = (plaintext[i] - 'A' + kkey) % 26 + 65;
}
printf("%c", plaintext[i]);
}
printf("\n");
return 0;
}
Thank you very much for your help.
As said by #Gerhardh, you can't use strings as argument of isalpha, you need a loop to check each character of the string.
In any case that is not the best approach, using a negated isdigit would be a better option, because it accounts for all the other non numeric characters.
//...
// Check if correct command-line arguments
if (argc != 2 || atoi(argv[1]) < 0)
{
printf("./caesar key\n");
return 1;
}
for(size_t i = 0; i < strlen(argv[1]); i++){
if(!isdigit(argv[1][i])){ //if one of the characters is not a digit 0-9
puts("./caesar key\n");
return 1;
}
}
kkey = atoi(argv[1]); //no else needed
//...
Note that atoi will invoke undefined behavior if the converted value is not representable by an int.
You can use strtol for a more robust alternative.
The link is for Linux man page which I find quite nice, but this is cross-platform.
Again, as stated by #Gerhardh, using character codes may backfire, in this case you are using ASCII encoding, but there are others, this makes your code less portable, use the character instead, 26 + 'a' and 26 + 'A'.

Why does adding a char (from argv) cause a no-result in C

first post (as you would know) and the form tells me that my title is bad and will get downvoted but I can't do better :) I've worked on this a bunch already.
I'm trying to cipher text by adding a number given in the command line to a string.
Why does
include <stdio.h>
int main(int argc, char * argv[])
{
printf("%i", argc);
char k = argv[1][0];
printf("%c", k);
char * s = "a";
printf("%c", s[0] + 0);
}
correctly print "a" (besides for printing argc and k)
but when the last line is
printf("%c", s[0] + k);
it just prints argc and k and then nothing. I wanted it to print 20a (when running ./question 0).
( I tried
char k = 0;
char * s = "a";
printf("%c", s[0] + k);
and that does work :( )
It seems like the fact that k is coming from argv is the issue but I don't have any idea why (not that I really understand any of this)
Thanks!
argv and argc and "a" are all red herrings. They have nothing to do with the problem. The problem lies in not understanding the difference between two character values, '0' and 0 (the latter is aso known as '\0').
In order to understand the difference I suggest experimenting with this program:
#include <stdio.h>
int main(void) {
char a = 'a', b = '0';
printf ("as char: a=%c b=%c sum=%c\n", a, b, a+b);
printf ("as int: a=%d b=%d sum=%d\n", a, b, a+b);
}
Use different values for b, including 0, 1, '0' and '1' and see what happens.
The answer is very simple.
The ASCII code of 'a' is 97.
Assuming only the printable characters in the arguments, the lowest printable ASCII code from the argv is 32.
97+32 = integer overflow which is UB - on ideone.com system it is -127
you can try yourself
https://ideone.com/D8DLcy
#include <stdio.h>
int main(void) {
for(char x = 32; x < 127; x++)
{
char m = 'a' + x;
printf("'a' + %c = char value in decimal is:%hhd - and the char is%c\n", x, m, m);
}
return 0;
}
The problem is that the expression s[0] + k is that adding a letter and a number together (conceptually speaking, ignoring the data types for a minute) can easily result in a value that is past the end of the alphabet, and printing it out as if it was a letter isn't necessarily going to work.
Bonus problem: reading the command line parameter into a single character isn't giving you a value of 0, but '0' which has a numerical value of 48. You can't just pull out the first character from the string. What if the user runs the program and passes 10 as a parameter?
You need to make sure that the result of your calculation is within the bounds of the alphabet.
As suggested by commenters above, take a look at the ASCII table, and try using some character constants sich as 'a' 'z' 'A' and 'Z' to modify the result
From the information you provided, it looks like you're implementing a simple shift cipher. Note the use of modulo for cases where the input for k is greater than 25.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define MAX_STRING 16
int main(int argc, char *argv[])
{
char num[MAX_STRING];
char * s = "a";
int k;
if (argc == 2)
{
snprintf(num,MAX_STRING, argv[1], 10);
k = (int)strtol(num, NULL, 10) % 26;
printf("%c\n", s[0] + k);
}
else
{
fprintf(stderr,"Invalid number of arguments\n");
return(EXIT_FAILURE);
}
return(EXIT_SUCCESS);
}

How to detect space and letters in a Char in C?

Do you know how I can detect space and letters in a CHAR variable?
I need to detect letters or space in a input of numbers:
This what I want to do:
Enter Document Number of 8 numbers:
// i press space and pressed enter
ERROR: please enter the age again: 4fpdpfsg
There's where my code doesn't detect the letters after the 4, and what I want is recognize that there's letters in the input, and then shows only the 4.
int isLetter(char input[]){
int i = 0;
while(input[i]!='\0'){
if((input[i]!=' ') && (input[i]<'a'||input[i]>'z') && (input[i]<'A'||input[i]>'Z'))
return 0;
i++;
}
return 1;
}
The standard C library has various character type testing functions. They are declared in the #include <ctype.h> header.
Unfortunately, the obvious way of using these functions is often wrong. They take an argument of type int which is actually expected to be an unsigned character value (a byte, effectively) in the range 0 to UCHAR_MAX. If you pass in a char value which happens to be negative, undefined behavior ensues, which might work by coincidence, crash or worse yet form a vulnerability similar to heartbleed (possibly worse).
Therefore the cast to (unsigned char) is quite likely necessary in the following:
#include <ctype.h>
/* ... */
char ch;
/* ... */
if (isalpha((unsigned char) ch) || ch == ' ') {
/* ch is an alphabetic character, or a space */
}
Simple character constants (not numeric escaped ones) derived from the C translation time character set have positive values in the execution environment; code which can safely assume that it only manipulates such characters can do without the cast. (For instance, if all the data being manipulated by the program came from string or character literals in the program itself, and all those literals use nothing but the basic C translation time character set.)
That is to say, isalpha('a') is safe; a is in the C translation time character set, and so the value of the character constant 'a' is positive. But say you're working with source code in ISO-8859-1 and have char ch = 'à';. If char is signed, this ch will have a negative value, which is fine according to ISO C because an accented à isn't in the basic C translation character set. The expression isalpha(ch); then passes a negative value to the isalpha function, which is wrong.
Try:
if (!((input[i] == ' ') || (input[i] >= 'a' && input[i] <= 'z') || (input[i] >= 'A' && input[i] <= 'Z')))
or, better:
#include <ctype.h>
if (!((input[i] == ' ') || isalpha(input[i])))
You could use sscanf(input,"%d%n",&number,&nrOfDigits) which reads in an integral value into number and additionally stores the position of the first character which has not been part of the number in nrOfDigits. With this information, you can then decide what to do, e.g. nrOfDigits < 8 would indicate that either the input was shorter than 8 characters, or that it does contain less than 4 consecutive digits. See sample code of the usage below.
#include <stdio.h>
#include <stdlib.h>
#include <ctype.h>
int isLetter(char input[]){
int nrOfDigits=0;
int number;
int scannedElems = sscanf(input,"%d%n",&number,&nrOfDigits);
if (scannedElems == 0) {// number could not be read--
printf ("No number read.\n");
return 0;
}
else {
char c = input[nrOfDigits];
int isAlpha = isalpha(c);
printf("input %s leads to number %d with %d digit(s); first characer after the digits is '%c', (isalpha=%d)\n", input, number, nrOfDigits, c, isAlpha);
return number;
}
}
int main(){
isLetter("4fpdpfsg"); // input 4fpdpfsg leads to number 4 with 1 digit(s); first characer after the digits is 'f', (isalpha=1)
isLetter("afpdpfsg"); // No number read.
isLetter("12345678"); // input 12345678 leads to number 12345678 with 8 digit(s); first characer after the digits is '�', (isalpha=0)
return 0;
}
BTW: you could implement a similar logic with strtoul as well.
hey guys i finally get the way to detect the input is conformed only for 8 numbers theres the code
char* InputDni(char dni[])
{
int sizeletter;
int i;
fflush(stdin);
gets(dni);
// 8 is the size of DNI in argentina
while((isLetter(dni)) || (strlen(dni)!=8))
{
printf("ERROR: enter again the DNI: ");
fflush(stdin);
gets(dni);
}
sizeletter=strlen(dni);
for(i=0 ;i<sizeletter; i++)
{
while(isalpha(dni[i]))
{
printf("ERROR: enter again the DNI: ");
fflush(stdin);
gets(dni);
i++
}
}
return dni;
}
//isLetter
int isLetter(char input[])
{
int i = 0;
int sizeletter;
int flag=1;
sizeletter=strlen(input);
for(i=0;i<sizeletter;i++)
{
if((input[i]!=' ') && (input[i]<'a'||input[i]>'z') && (input[i]<'A'||input[i]>'Z'))
{
flag=0;
}
}
return flag;
}
picture of the code running in cmd:

C: Output with symbols in Caesar’s cipher encrypts, WHY? pset2 cs50

This is Caesar’s cipher encrypts problem in pset2 of cs50x course in edx.org.
I already solved this problem with another algorithm but this was my first try and I'm still curious why appear all these symbols at the right side of the caesar text.
ie. I enter the text "Testing" and the output is "Fqefuz�����w����l��B��" but the answer is correct without the symbols.
Can anyone explain me that?
int main(int argc, string argv[])
{
bool keyOk = false;
int k = 0;
do
{
if(argc != 2) // Checking if the key was correctly entered.
{
printf("You should enter the key in one argument from"
" the prompt(i.e. './caesar <key>').\n");
return 1;
}
else
{
k = atoi(argv[1]); // Converting string to int.
keyOk = true; // Approving key.
}
}
while(keyOk == false);
string msg = GetString(); // Reading user input.
char caesarMsg[strlen(msg)];
for(int i=0, n = strlen(msg); i < n; i++)
{
if( (msg[i] >= 'a') && (msg[i] <= 'z') )
// Processing lower case characters
{
caesarMsg[i] = ((((msg[i] - 97) + k) % 26) + 97);
}
else if( (msg[i] >= 'A') && (msg[i] <= 'Z') )
// Processing upper case characters
{
caesarMsg[i] = ((((msg[i] - 65) + k) % 26) + 65);
}
else
{
caesarMsg[i] = msg[i];
}
}
printf("%s", caesarMsg);
printf("\n");
}
The root problem is C does not have a full, proper, or first-class "string" datatype. In C strings are in fact character arrays that are terminated with the NUL ('\0') (*) character.
Look at
string msg = GetString(); // Reading user input.
char caesarMsg[strlen(msg)];
This is equivalent to
char* msg = GetString(); /* User or library function defined elsewhere */
/* calculates the length of the string s, excluding the terminating null
byte ('\0') */
size_t len = strlen(msg);
char caesarMsg[len]; /* Create an character (byte) array of size `len` */
Hopefully this makes it clearer, why this section fails to work correctly. The variable len that I've added, is the length of the sequence of non-NUL characters in the string msg. So when you create the character array caesarMsg of length len, there is no room for the NUL character to be stored.
The for loop correctly executes, but the printf("%s", caesarMsg); will continue to print characters until it finds a NUL or crashes.
BTW you can reduce the two printf statements at the end into a single printf statement easily.
printf("%s\n", caesarMsg);
Strings and character arrays are a frequent source of confusion to anyone new to C, and some not-so-new to C. Some additional references:
I really recommend bookmarking is the comp.lang.c FAQ.
I also strongly that you have either get your own copy or ensure you have access to Kernighan and Ritchie's The C Programming Language, Second Edition (1988).
Rant: And whoever created the string typedef is evil / making a grave error, by misleading students that into thinking C's strings are are a "real" (or first-class) data type.
(*) NUL is different from NULL, because NULL (the null-pointer) is cast as a pointer as so it the same size as other pointers, where as NUL is a null-character (and either the size of a char or int).

Resources