I'm testing deciphering strings using a key in standard C. I'm inputting a string, inputting my replacements and constructing a cipher based on the history of my replacements. For example, if I replace c with h in a string, I will store it in the string cipher as "ch ", where " denotes the start and end of my string.
My problem is, as I'm trying to run back through my string and revert characters back to their originals, my characters are messing up and showing as heart signs instead. Any insight on this would be much appreciated, I'm sure I'm going wrong somewhere very simple.
char from[] = "";
char to[] = "";
for (int i = 0; i <= strlen(cipher); i++){
if (i == 0){
from[strlen(from)] = cipher[i];
}
else if(cipher[i-1] == ' '){
from[strlen(from)] = cipher[i];
}
else{
to[strlen(to)] = cipher[i];
}
}
for (int i = 0; i <= strlen(str); i++){
for(int j = 0; j <= strlen(to); j++){
if(str[i] == to[j]){
str[i] = from[j];
}
}
}
printf("\nUNSCRAMBLED STRING IS: %s" ,str);
cipher is the char array holding my cipher key. to[] hold the second value in the cipher key. from[] holds my first. str is the char array of the string that is to be deciphered.
You have a number of problems.
(1) One big problem is that you don't have enough storage allocated for from and to - change:
char from[] = ""; // this is equivalent to char from[1] = "";
char to[] = "";
to e.g.:
char from[256] = "";
char to[256] = "";
(where 256 is just some arbitrary size that should be greater than the size of your data set).
(2) You need to terminate your strings properly when you append a character, e.g.
if (i == 0){
from[strlen(from)] = cipher[i];
}
should be:
if (i == 0){
int len = strlen(from);
from[len] = cipher[i];
from[len + 1] = '\0';
}
(3) You're indexing by one too many characters when you iterate through a string, so e.g.
for (int i = 0; i <= strlen(cipher); i++){
should be:
for (int i = 0; i < strlen(cipher); i++){
Related
For some reason when I change the copy of string s that is (string c), string s also gets changed and for loop j keeps on looping until it reached position zero in string alphabet which is 'a'.
int key = atoi(keys);
string s = get_string("plaintext: ");
string c = s;
int length = strlen(s);
string alphabet = "abcdefghijklmnopqrstuvwxyz";
string ALPHABET = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
int position = 0;
for (int i = 0; i < length; i++)
{
printf("Before Conversion: %c\n",s[i]);
for (int j = 0; j < 26; j++)
{
if(s[i] == alphabet[j])
{
position = (j + key) % 26;
c[i] = alphabet[position];
printf("Ascii: %i + Key: %i.. Position = %i\n",j,key,position);
position = 0;
printf("Not Converted: %c\n",s[i]);
printf("After conversion: %c\n\n",c[i]);
}
if(s[i] == ALPHABET[j])
{
position = (j + key) % 26;
c[i] = ALPHABET[position];
printf("Ascii: %i + Key: %i.. Position = %i\n",j,key,position);
position = 0;
printf("After conversion: %c\n\n",c[i]);
}
}
}
printf("ciphertext: %s\n",c);
Change:
string s = get_string("plaintext: ");
string c = s;
to
string s = get_string("plaintext: ");
string c = malloc(strlen(s) + 1);
strcpy(c, s);
That will make it work. The biggest problem here is that the course CS50 is using
typedef char* string;
to hide the complexity of pointers for new students, but in reality it just causes confusion. In order to fully understand why the above solution works, you need to understand pointers and dynamic allocation.
Let's say I have a series of data that's in this form:
"SomethingIDontCareAbout : SomethingICareAbout"
where the part after the ":" can vary in length of course.
The goal here is only storing the "SomethingICareAbout" substring efficiently. I made this function but the problem is that I'm storing both substrings,so it seems like a waste of memory. Any help to reduce to the time/space complexity?
char** ExtractKey(char* S)
{
int n = strlen(S);
int count = 0, i = 0, j = 0;
for(i = 0; i < n; i++)
{
if(S[i] == ':')
break;
count++;
}
char** T = (char**)malloc(2 * sizeof(char*));
T[0] = (char*)malloc((count + 1) * sizeof(char));
T[1] = (char*)malloc((n - count) * sizeof(char));
for(i = 0; i < count; i++) // inefficient ? cus we won't need T[0] [j]
{
T[0][j] = S[i];
j++;
}
T[0][j+1] = '\0';
j = 0;
for(i = count + 1; i < n; i++)
{
T[1][j] = S[i];
j++;
}
T[1][j+1] = '\0';
return T;
}
There is no reason to invent a search for a character in a string, or a copy of a string.
If the input data will live long enough for you to use the "value" part, just return a pointer to the value:
char* ExtractKey(char* S)
{
return strchr(S, ':');
}
If it doesn't, or if you for some reason need a separate copy:
char* ExtractKey(char* S)
{
return strdup(strchr(S, ':'));
}
Honestly, this could be done efficiently if strtok() was used to split those strings. I have designed the following code that parses each string of a 2-D array with a common delimiter that is : here.
Now, let's take a look into the code (notice the comments):
#include <stdio.h>
#include <string.h>
#define MAX_LEN 128
int main(void) {
// The 2-D string
char str[][MAX_LEN] = {"SomethingElse : SomethingToCareAbout",
"Something2 : SomethingToCare2",
"Unnecessary : Necessary"};
int size = sizeof(str) / sizeof(str[0]);
// Applying Variable-Length Array (valid in C)
char store_cared_ones[size][MAX_LEN];
for (int i = 0; i < size; i++) {
// Declaring a temporary pointer variable to obtain the required
// substring from each string
char *sub_str = NULL;
sub_str = strtok(str[i], ": ");
sub_str = strtok(NULL, ": ");
// Copying the 'sub_str' into each array element of 'store_cared_ones'
strcpy(store_cared_ones[i], sub_str);
}
// Displaying each of 'store_cared_ones'
for (int i = 0; i < size; i++)
fprintf(stdout, "%s\n", store_cared_ones[i]);
return 0;
}
Finally, let's see what that code does:
rohanbari#genesis:~/stack$ ./a.out
SomethingToCareAbout
SomethingToCare2
Necessary
Here's the overview: I have to make a cipher that can decrypt and encrypt messages(I have completed that), but the reason we have these ciphers are to encrypt/decrypt messages that are sentences which have whitespaces. Basically, I just need to know when there is whitespace, and when there is, to add that whitespace to the final output.
#TL;DR:# How to add whitespace to a String - char output[] ~~~~~~~~~~ that's the String
for(int j = 0; input[j] != '\0'; j++){
int at_index = 0;
int i = 0;
//need to check at this point for whitespace
if(input[j] == ' '){
output[j] = ' ';
}
//gives garbage value
for(i; alphabet[i] != input[j]; i++){
++at_index;
}
output[j] = alphabet[at_index + shift];
}
The error you made is that you don't stop the handling of the character after recognizing the whitespace.
This is one possible solution, derived from your source:
for (int j = 0; input[j] != '\0'; j++) {
if (isspace(input[j])) {
output[j] = input[j];
} else {
for (int i = 0; alphabet[i] != input[j]; i++) {
}
output[j] = alphabet[i + shift];
}
}
Some notes:
at_index is always the same value as i so it's not needed.
You need to add some logic if the input character is not in the alphabet. Since I don't know how your alphabet is defined, I can't show it here.
The for-loop can be replaced by some appropriate string function.
You need some logic to prevent an out-of-bounds access to alphabet when reading the shifted character.
I would call a helper function in order to achieve this,
white_spaces(char *dest, int size, int num_of_spaces) {
int len = strlen(dest);
// for the check i still assume dest tto contain a valid '\0' terminated string, so len will be smaller than size
if( len + num_of_spaces >= size ) {
num_of_spaces = size - len - 1;
}
memset( dest+len, ' ', num_of_spaces );
dest[len + num_of_spaces] = '\0';
}
I am trying to capture a user input string, then display that string in reverse order next to the initial string. My code is as follows:
char str[300], revstring[300];
int i, strlen;
int main(void) {
printf("Enter a string: "); //Prompt user for input string
gets(str);
for (i = 0; str[i] != NULL; i++) { //Get length of string
strlen += 1;
}
for (i = 0; i <= strlen; i++) {
revstring[i] = str[strlen - i];
}
printf("\n\nThe palindrome of your input is %s%s\n\n\n", str, revstring);
return 0;
}
When I run the program however, I see nothing after the initial string. I come from a python background so maybe I am thinking about this in too much of a python mindset, but I feel like this should work.
After this loop
for (i = 0; str[i] != NULL; i++) { //Get length of string
strlen += 1;
}
str[strlen] is equal to the terminating zero '\0'. And the next loop starts from writing this zero in the first element of the array revstring when i is equal to 0.
for (i = 0; i <= strlen; i++) {
revstring[i] = str[strlen - i];
}
As result nothing is displayed.
Also you should not forget to append the result string with the terminating zero.
Take into account that the function gets is unsafe and is not supported any more by the C Standard. It is better to use the standard function fgets. But using it you should remove the appended new line character.
The program can be written the
#include <stdio.h>
#define N 300
int main( void )
{
char str[N], revstring[N];
printf( "Enter a string: " );
fgets( str, N, stdin );
size_t length = 0;
while ( str[length] != '\0' && str[length] != '\n' ) ++length;
if ( str[length] == '\n' ) str[length] = '\0';
size_t i = 0;
for ( ; i != length; i++ ) revstring[i] = str[length - i - 1];
revstring[i] = '\0';
printf("\n\nThe palindrome of your input is %s%s\n\n\n", str, revstring);
return 0;
}
Its output might look like
Enter a string: Hello, Froobyflake
The palindrome of your input is Hello, FroobyflakeekalfyboorF ,olleH
The string is a null-terminated string. You are copying the null character to the beginning of the reversed string. This tells the system that they string is terminated at the first character.
You could use this code instead.
for (i = 0; i < strlen; i++)
{
revstring[i] = str[(strlen - 1) - i];
}
revstring[strlen] = 0;
Here only the characters before the null character are copied and then the null character is added at the end.
I have two strings:
char *str1 = "this is a test";
char *str2 = "ts bd a";
I'm trying to write a function that returns a new string with the same chars from the two string without duplicates (also ' ' is duplicate). eg.:
char *retStr = GetSameChars(str1, str2); //returns "ts a";
How can I do that?
What I'm tried:
char *GetSameChars(char str1[], char str2[]) {
int found = -1, i , j = 0, biggest, index = 0;
char *retArr, *star = '*';
int str1Len, str2Len, count = 0;
str1Len = strlen(str1);
str2Len = strlen(str2);
biggest = str1Len > str2Len ? str1Len : str2Len;
retArr = (char *)malloc(sizeof(char) * count);
for (i = 0; i < str1Len; i++) {
for (j = 0; j < str2Len; j++) {
if (str1[i] == str2[j] && found == -1) {
count++;
found = j;
} else
if (str2[j] == str2[found])
str2[j] = star; //Throw an exception
}
found = -1;
}
retArr = (char *)malloc(sizeof(char) * count);
j = 0;
for (i = 0; i < str2Len; i++)
if (str2[i] != '*')
retArr[j++] = str2[i];
for (i = 0; i < str2Len; i++)
printf("%c", retArr[i]);
}
When I tried the line str2[j] = star; I got an exception.
What is my mistake?
My recommendations would be: keep it simple; get to know the C standard library; write less, test more.
Some specific problems with your code: you pass the wrong variable to malloc(); you estimate the answer to fit in the size of the larger of the two strings but it will actually fit into the smaller of the two; you modify an argument string str2[j] = star -- you should be treating the arguments as readonly; you malloc() retArr twice unnecessarily, leaking the first one when you allocate the second; your algorithm simply doesn't work.
Although a lookup table, as others have suggested, would be more efficient, let's use the standard library routine strchr() to solve this problem:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
char *getSameChars(const char *string1, const char *string2) {
size_t string1_length = strlen(string1);
size_t string2_length = strlen(string2);
size_t shortest = string1_length < string2_length ? string1_length : string2_length;
char *common_pointer, *common = malloc(shortest + 1);
*(common_pointer = common) = '\0';
for (size_t i = 0; i < string1_length; i++) {
// character found in both input strings, but not yet in common string
if (strchr(string2, string1[i]) != NULL && strchr(common, string1[i]) == NULL) {
*common_pointer++ = string1[i];
*common_pointer = '\0';
}
}
return common;
}
int main() {
char *stringA = "this is a test";
char *stringB = "ts bd a";
char *result = getSameChars(stringA, stringB);
printf("%s\n", result);
free(result);
return(0);
}
Your code complains because you are trying to assign a pointer to a char, to get the value inside a pointer you need to use the * operator like so:
*star;
a good way to check if a letter have already appeared(if you want to use it on all of the ascii table then 128) is to use a lookup table. first you will need to declare an array the length of all letters in the alphabet like so:
char lut[26];
If it is a global variable then it will be set to 0, then all you need to do is go to the index of the char you got and mark it as 1, a simple if will later be able to determine if a letter has already appeard.
example:
lut[toupper(somechar) - 'A'] = 1;
In this example you set the char in the lookup table that is equivalent to the somechar variable as 1, marking it has already appeared.
hope this helps.