So im trying to code a problemset from cs50, in which i have to convert a message into a secret message with all the letters mixed up.
#include <cs50.h>
#include <stdio.h>
#include <ctype.h>
int main(int argc, char *argv[])
{
string key = argv[1];
int werte_key[sizeof key];
for (int i= 0; i<sizeof key; i++)
{
printf("%c\n", key[i]);
}
//checks ob alle Vorraussetzungen erfüllt sind:
//Wenn der eingegebene key nicht 26 Stellen lang ist:
if (sizeof key != 26)
{
printf("%i\n", (int) sizeof key);
printf("Key must contain 26 characters.\n");
return 1;
}
sadly my code messes up instantly. My input is ./substitution ACBEDGFIHKJMLONQPSRUTWVZXY but when i let the code printyour text out my input via
for (int i= 0; i<sizeof key; i++)
{
printf("%c\n", key[i]);
}
i get A C B E D G F I 8 and after that the error message "Key must contain 26 characters.".
As you can probably tell im a beginner and im completely clueless as to why thats happening or what to search for in the internet to fix this problem.
I am sorry for my poor english as it is not my first language and thank you for everyone helping.
sizeof key key is wrong. That gives the number of bytes in the key object. The key object is not your string; it is a pointer to the first character of a string. Its type is char *. Its size in your C implementation is eight bytes.
To get the length of a string, use the strlen function, which is declared in the standard header <string.h>.
Related
Closed. This question does not meet Stack Overflow guidelines. It is not currently accepting answers.
This question does not appear to be about programming within the scope defined in the help center.
Closed 9 months ago.
Improve this question
I am now trying to change the return type of function encrypt to char (or string if necessary) instead of void. Although this code already works (and btw, I have two other versions of code that works too) I really want to grasp the fundamental concept of how return type works and so on. Also, I'm wondering how line 33 char *ciphertext = plaintext; can be modified in a way that the array does not hold any address or values (if, for example, drops the pointer(*)) but assign the size like char *ciphertext[strlen(plaintext)]? This is not a necessary modification needed since the code works totally fine, but if I use this kind of style, and in a given situation where I need to store massive volume of data into the array, it will slow down the speed.
So, that was exhaustive. To make it simple, here are the two questions:
How can I modify the function encrypt return type to char or string?
How can array ciphertext get initialized with assigned size identical to the plaintext while data(value) is not stored?
I deeply appreciate your help, answer, comment (feel free to criticize if the style of my code isn't clean or pleasant to read).
#include <cs50.h>
#include <stdio.h>
#include <ctype.h>
#include <string.h>
#include <stdlib.h>
// Trying char as a return type
void encrypt(char *plaintext, int k, char *ciphertext);
int main(int argc, char *argv[])
{
// Validate if the user inputted the adequate value type
if (argc != 2)
{
printf("Usage: ./caesar key\n");
return 1;
}
// Loop for validating if each char of argv is a digit
for (int i = 0, n = strlen(argv[1]); i < n; i++)
{
// if the key is not a digit, it will return 0.
if (isdigit(argv[1][i]) == 0)
{
printf("Usage: ./caesar key\n");
return 1;
}
}
// function atoi converts key(array of char) into an integer
int key = atoi(argv[1]);
char *plaintext = get_string("plaintext: ");
char *ciphertext = plaintext;
encrypt(plaintext, key, ciphertext);
printf("ciphertext: %s\n", ciphertext);
return 0;
}
void encrypt(char *plaintext, int k, char *ciphertext)
{
while (*plaintext) {
// Identify if the value is an alphabet
if (isalpha(*plaintext) != 0)
{
// Apply formula to uppercase character
if (isupper(*plaintext) != 0)
{
*ciphertext = ((*plaintext - 'A' + k) % 26) + 'A';
}
// Apply formula to lowercase character
else
{
*ciphertext = ((*plaintext - 'a' + k) % 26) + 'a';
}
}
plaintext++;
ciphertext++;
}
}
As mentioned in the comments, you are writing to a NULL pointer since you set ciphertext to NULL. What you want to do is allocate memory for ciphertext. This can be done with char *ciphertext = strdup(text); which sets up ciphertext with a newly allocated pointer to a copy of the null-terminated char pointer named text. Using either this solution or the one below, you don't need to // Store non-alphabetical value as it is since that value is already there.
Using strdup() is one method, but there is a better option if you can modify text itself. You can simply use:
void encrypt(char *text, int k) {
while (*text) {
// Identify if the value is an alphabet
if (isalpha(*text) != 0) {
if (isupper(*text) != 0) {
// Apply formula to uppercase character
*text = ((*text - 'A' + k) % 26) + 'A';
} else {
// Apply formula to lowercase character
*text = ((*text - 'a' + k) % 26) + 'a';
}
}
text++;
}
}
A little explanation: while (*text) means do the following until the value stored at text is 0. text++; increments the pointer. So after that line text is pointing to the next char. All that does is just another way of doing for (int i = 0; text[i] != '\0'; i++) but without needing a new variable.
Note that the above code no longer returns the result of the "encryption". Rather, it modifies the content of the char * you passed it. No memory allocation necessary-- the caller did it for you.
If you opt to use the above code, you'll need to do something like this to print the result:
...
char *text = get_string("plaintext: \n");
encrypt(text, key);
printf("ciphertext: %s\n", text);
...
I'm trying to write a program that uses Caesar's algorithm to cipher a string input. I'm a beginner to C but I can understand basic codes. So to cipher the text I wrote this code, but when I enter the input, I get an error that says
Segmentation fault (core dumped)
I tried to do some debugging by removing the else condition at the end and the program kind of worked for short inputs of 2-3 letters
Can someone please help me with this issue?
I'm using the CS50's header only to get the string in the first place.
#include <stdio.h>
#include <cs50.h>
#include <string.h>
#include <ctype.h>
#include <stdlib.h>
int main(int argc, char * argv[])
{
char name[] = "";
strcat(name, argv[1]);
int key = atoi(name);
string plaintext = get_string("plaintext: ");
int length = strlen(plaintext);
char ciphertext[] = "";
for(int i = 0; i < length; i++)
{
int skipCount = 0;
if(isalpha(plaintext[i]))
{
while(skipCount < key)
{
char tmp = (char) ((int) plaintext[i] + 1);
if(isalpha(tmp))
{
ciphertext[i] = tmp;
skipCount++;
}
else
{
if (isupper(plaintext[i]))
{
tmp = 'A';
skipCount++;
}
if (islower(plaintext[i]))
{
tmp = 'a';
skipCount++;
}
}
}
}
else ciphertext[i] = plaintext[i];
}
printf("%s\n", ciphertext);
}
What you need to understand about C, is that it does not automatically allocate memory.
You have to it your self!
This line:
char name[] = "";
creates an array of size 1, which holds a single character - the "null" character = '\0';
It signifies an empty string.
You can not copy any larger string in to it, because all strings in C must have a null character at the end, so there isn't enough room for even a single readable character.
As a beginner, you would need to decide what the maximum length of the string you want will be, and declare the array to be of proper size:
char name[255];
This is one example that can hold up to 254 characters, plus the terminating null character.
Here is the code:
#include <cs50.h>
#include <stdio.h>
#include <ctype.h>
#include <string.h>
int main(int argc, string argv[])
{
string key = argv[1];
string keyupper = argv[1];
string keylower = argv[1];
if (argc != 2) //makes sure there is exactly 2 arguments (the program executable and the key)
{
printf("Please input a key.\n");
return 1;
}
else if (strlen(key) != 26) //makes sure the key is exactly 26 letters
{
printf("Please make sure the key is 26 unique letters.\n");
return 1;
}
for (int i = 0; i < 26; i++) //the loop to make the uppercase key
{
keyupper[i] = toupper(keyupper[i]);
}
for (int i = 0; i < 26; i++) //the loop to make the lowercase key
{
keylower[i] = tolower(keylower[i]);
}
Essentially, I want to make a very basic encryption using a key entered while executing the program, it needs to contain 26 unique letters. I want to create two arrays, an uppercase and a lowercase one, to make everything else much easier for me, but when running this code, all keys become either uppercase or lowercase depending on which loop is created last (in this case, they all become lowercase). Even key gets changed to lowercase even though it's used only once as a declaration. Everything else works but this.
This is for the CS50 course so functions such as toupper() are included in libraries.
This is my first ever question so sorry if it's worded poorly. Thank you!
Code failed to copy the string contents
[Talking about string here, not the type string]
In C, a string is "... is a contiguous sequence of characters terminated by and including the first null character."
Code only copied pointers and not the string contents.
string key = argv[1];
string keyupper = argv[1];
string keylower = argv[1];
Comment discussion indicates OP now sees why code is in error.
Repaired code
//#include <cs50.h>
#include <stdio.h>
#include <ctype.h>
#include <string.h>
// Avoid naked magic numbers, instead define them
#define KEY_N 26
int main(int argc, string argv[]) {
// Do not code argv[1] until after argc check
// string key = argv[1];
// string keyupper = argv[1];
// string keylower = argv[1];
if (argc != 2) {
printf("Please input a key.\n");
return 1;
}
char *key = argv[1];
// else if (strlen(key) != 26)letters
if (strlen(key) != KEY_N) {
printf("Please make sure the key is 26 unique letters.\n");
return 1;
}
char keyupper[KEY_N + 1];
char keylower[KEY_N + 1];
// for (int i = 0; i < 26; i++)
for (size_t i = 0; i < KEY_N; i++) {
// keyupper[i] = toupper(keyupper[i]);
keyupper[i] = toupper((unsigned char) key[i]);
}
keyupper[KEY_N] = '\0';
...
I really appreciate any help/advice provided! Am noob.
This program is supposed to take text and cipher it using a key the user inputs. So when the user runs the program with their 26 letter key, the program asks them for plaintext (Whatever they want ciphered), then the program should output the ciphered text. I left out the validation & append functions in this question.
=====
My question is, in the for loop, alphabetic letters work fine. The issue is when there are NON-ALPHABETIC chars, the program returns a segmentation fault.
Does anyone have any idea why this happens, or how I can fix this? In the meantime I will try ctype.h's "ispunc" "isspace" etc etc. Just wondering if there is a BASIC other way to do it that is shorter than typing that all out...we haven't learned pointers yet.
Let me know if any further info is needed...
// test key: ./substitution JTREKYAVOGDXPSNCUIZLFBMWHQ
// test 2: ./substitution VcHpRzGjNtLsKfBdQwAxEuYmOi
// test 3: ./substitution vchprzgjntlskfbdqwaxeuymoi
#include <cs50.h>
#include <stdio.h>
#include <string.h>
#include <ctype.h>
int validatekey(int argc, string argv[]);
void append(char* s, char c);
int main(int argc, string argv[]) // argc = # of command line args (name of program & input), argv saves them both.
{
if (validatekey(argc, argv) == 0) //everything is fine
{
string key = argv[1]; //user's inputted key
int keylength = strlen(key);
string plaintext = get_string("plaintext: ");
string plainkey = "abcdefghijklmnopqrstuvwxyz";
int plength = strlen(plaintext);
char cipher[plength]; //outputted ciphertext
for (int i = 0; i < plength; i++)
{
char p = tolower(plaintext[i]); //make plaintext lowercase so things match up
char *where_is_p = strchr(plainkey, p);
int index = (int)(where_is_p - plainkey); // get index of letter in plainkey
char c = key[index]; //corresponding cipher index location
if (isalpha(plaintext[i])) // check if letter is ALPHABET first
{
if (isupper(plaintext[i])) // if current plaintext char is uppercase
{
//append(cipher, toupper(c));
printf("is upper");
}
else if (islower(plaintext[i])) // if it's lowercase
{
//append(cipher, tolower(c));
printf("is lower");
}
}
else //(isalpha(plaintext[i]) == 0 ) // if it's not a-z NOTE: SEGMENTATION FAULT
{
//append(cipher, plaintext[i]);
printf("not alpha");
}
}
//printf("ciphertext: %s\n", cipher);
}
}
My question is, in the for loop, alphabetic letters work fine. The issue is when there are NON-ALPHABETIC chars, the program returns a segmentation fault.
Does anyone have any idea why this happens,
strchr(plainkey, p); returns NULL when p not found in plainkey. Code below dies on pointer subtraction and out of range array index.
char *where_is_p = strchr(plainkey, p);
int index = (int)(where_is_p - plainkey);
char c = key[index];
or how I can fix this?
When where_is_p == NULL, perform the is-not-alpha code.
BTW, char cipher[plength]; is 1 too small to form a string of ciphered text.
Im doing a railroad cipher (zigzag cipher) however you may call it, I finally seemed to get my code to work properly and I got it to print the correct output, but unfortunately my teacher calls for the output to be printed 80 columns wide (80 characters per line). Unfortunately, the way my encryption is set up I can not find a way to do this since I set my encryption "rail by rail".
For the assignment we must read in the file, and strip it of all spaces and special characters, and to make all uppercase letters lower-case. Then encrypt the message. My issue is the printing portion in the encrypt function.
since its ran from command line here are the files i used
the first file pointer is for the rails, sample would be: 9
second file pointer is the text, sample i used is:
We shall not flag or fail. We shall go on to the end. We shall fight in France, we
shall fight on the seas and oceans, we shall fight with growing confidence and
growing strength in the air, we shall defend our island, whatever the cost may be, we
shall fight on the beaches, we shall fight on the landing grounds, we shall fight in
the fields and in the streets, we shall fight in the hills. we shall never surrender!
my output is correct according to my teachers output, but unfortunately i get 30% off for not having it 80 characters per line... this is due in a few hours but I can't seem to figure it out. Any help is greatly appreciated.
I would show the output for reference but I don't know how to copy and paste from the command line, and it only runs from there.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
# define MAX 10000
void condense(char* str)
{
int original=0;
int newplace =0;
while (str[original] != '\0')
{
if(isalpha(str[original] ))
{
str[newplace]= tolower(str[original]);
++newplace;
}
++original;
}
str[newplace] = '\0';
}
char * deblank(char *str)
{
char *out = str, *put = str;
for(; *str != '\0'; ++str)
{
if(*str != ' ')
*put++ = *str;
}
*put = '\0';
return out;
}
void encrypt(int rail,char *plain)
{
char railfence[rail][MAX],buf[2];
int i;
int number=0,increment=1;
buf[1]='\0';
for(i=0;i<rail;i++)
railfence[i][0]='\0';
for(i=0;i<strlen(plain);i++)
{
if(number+increment==rail)
increment=-1;
else if(number+increment==-1)
increment=1;
buf[0]=plain[i];
strcat(railfence[number],buf);
number+=increment;
}
for(i=0;i<rail;i++)
printf("%s",railfence[i]);
}
int main(int argc, char *argv[])
{
int rail,mode;
char text[MAX];
FILE* fp1;
FILE* fp2;
fp1 = fopen(argv[1], "r");
fp2 = fopen(argv[2], "r");
int key;
fscanf(fp1, "%d", &key);
printf("key is %d", key);
char c;
int index = 0;
fgets(text, 10000, fp2);
printf("%s \n", text);
// text[index] = '0';
char nospace[MAX];
deblank(text);
printf("text deblanked: %s \n", text);
//printf("%s", deblank(text));
condense(text);
printf("\nthe text condensed is: %s", text);
printf("\n the text encrypted is \n");
encrypt(key,text);
return 0;
}
Simple. Instead of printing each rail as a whole, print each rail character by character, and count. In the example below I assume your instructor's 80 characters per line is 79 characters of ciphertext plus one newline character. I do not know whether you are expected to print a newline at the end of the ciphertext, but if so just add printf("\n"); at the end of encrypt (though you might want to check that there was at least one character of ciphertext before doing so).
void encrypt(int rail,char *plain)
{
char railfence[rail][MAX],buf[2];
int i, col = 0, j, len; // added col, j, and len
int number=0,increment=1;
buf[1]='\0';
for(i=0;i<rail;i++)
railfence[i][0]='\0';
for(i=0;i<strlen(plain);i++)
{
if(number+increment==rail)
increment=-1;
else if(number+increment==-1)
increment=1;
buf[0]=plain[i];
strcat(railfence[number],buf);
number+=increment;
}
for(i=0;i<rail;i++)
{
len = strlen(railfence[i]); // get the length of the rail
for(j=0;j<len;++j) // for each char in the rail
{
printf("%c", railfence[i][j]);
if (++col==79) {
col = 0;
printf("\n");
}
}
}
}
Other than that, I thoroughly recommend using more whitespace in your formatting, as well as checking things like whether the user passes in two arguments or not, whether your files were opened successfully or not, and also remembering to close any files you open.
As result, your program is hard to read, and currently behaves badly if I do not provide both command line arguments or if I give it non-existent files.