cs50 - pset 2 - substitution program - c

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);.

Related

What is happening to the printf function?

I am attempting to write the substitution program here https://cs50.harvard.edu/x/2022/psets/2/substitution/
here is my code:
#include <cs50.h>
#include <stdio.h>
#include <string.h>
#include <ctype.h>
#include <math.h>
int main(int argc, string argv[1])
{
if (argc > 2 || argc < 2){
printf("Plz 1 word in command \n");
return 1;
}
int sum = 0;
string arg1 = argv[1];
//test
for (int i = 0; i < strlen(arg1); i++){
if (isalpha(arg1[i]) == 0){
printf("plz only alphabet character \n");
return 1;
}}
// convert all key to upper
for (int i = 0; i < strlen(arg1); i++){
arg1[i] = toupper(arg1[i]);
}
for (int i = 0; i < strlen(arg1); i++){
sum = sum + (int)(arg1[i]);
}
if (strlen(arg1) != 26){
printf("Plz input 26 char \n");
return 1;
} else if (sum != 2015){printf("no oveerlapping letter plz \n");
return 1; }
//test finish
string al = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
string pt = get_string("plaintext: ");
char pt1[strlen(pt)];
char cp[strlen(pt)];
// all plain text to upper
for (int i = 0; i < strlen(pt); i++){
pt1[i]=toupper(pt[i]);
}
//scan
for (int a = 0; a < strlen(pt); a++){
char b = pt1[a];
for (int i = 0; i < strlen(al); i++){
if ( al[i] == b){
cp[a] = arg1[i];
break;
} else {
cp[a] = b;
}
}
//case preserve
if (islower(pt[a])){
cp[a] = tolower(cp[a]);
}}
printf("ciphertext: %s \n", cp);
return 0;
}
when i type in the key YTNSHKVEFXRBAUQZCLWDMIPGJO and then type "hello!1 lmao" as plaintext, here is what i receive
substitution/ $ ./substitution YTNSHKVEFXRBAUQZCLWDMIPGJO
plaintext: hello!1 lmao
ciphertext: ehbbq!1 bayq�
it should only show ehbbq!1 bayq but it is showing more letter than i intended,
there might be other letter or simbol after "bayq", can someone explain to me what is going on and why there are additional text in my output?
You need a null terminatig character (usually it is character having integer value 0) to terminate the string
size_t a;
for (a = 0; a < strlen(pt); a++)
{
char b = pt1[a];
for (int i = 0; i < strlen(al); i++)
{
if ( al[i] == b)
{
cp[a] = arg1[i];
break;
} else {
cp[a] = b;
}
}
//case preserve
if (islower((unsigned char)pt[a]))
{
cp[a] = tolower((unsigned char)cp[a]);
}
}
cp[a] = 0;
you need to pass unsigned char to functions like tolower. I did not analyze the logic of your code as it is your home work.
Also cp is too short, it has to be char cp[strlen(pt) + 1];
Your char arrays as declared are too short to handle copies of the data, due to the need for a null-terminator att the end.
char pt1[strlen(pt)];
char cp[strlen(pt)];
Rather you need to do:
char pt1[strlen(pt) + 1];
char cp[strlen(pt) + 1];
However, the other approach would be to simply use strdup to dynamically allocate sufficient storage and copy the data.
char pt1 = strdup(pt);
char cp = strdup(pt);
Of course, any function that returns dynamically allocated memory (likely including cs50's get_string) means you should remember to free that memory. And ensure it actually succeeded.

Struggling with wraparound counting in C

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.

Why the loops has a problem (pset2 Substitution)

I have been working CS50X pset2 substitution. I think it almost has done. When I input single char such as A or B D and so on... the ciphertext will get the right result. (e.g A will get J, "B" will get T and D will get E and so on...
However, if I input ABC, the ciphertext will only show J and the other cannot show. What did I do wrong?
#include <cs50.h>
#include <stdio.h>
#include <ctype.h>
#include <string.h>
#include <stdlib.h>
int ciphertext = 0;
//Key
//JTREKYAVOGDXPSNCUIZLFBMWHQ
int main(int argc, string argv[])
{
//Check that program was run with one command-line argument
if (argc == 2)
{
string key = argv[1];
//check the key does it validate
for (int i = 0, n = strlen(key); i < n; i++)
{
string plaintext = get_string("plaintext: ");
printf("ciphertext: \n");
int u = 64;
for (int k = 0, p = strlen(plaintext); k < p; k++)
{
if (isupper(plaintext[k]) != 0)
{
for (int j = 0; j < 26; j++)
{
u = u + 1;
//printf("u is %c\n", u);
if (u == plaintext[k])
{
ciphertext = key[j];
printf("Key is %c\n", key[j]);
printf("Plaintext is %c\n", plaintext[k]);
printf("ciphertext is %c\n", ciphertext);
//break;
}
}
}
else
{
printf("%c", plaintext[k]);
}
}
return 0;
}
}
}
Your problems are logic errors that appear to result from changes you attempted to make to the code that got out of hand. You have unnecessary loops and unused variable in your code. You need to change the scope of the u declaration so it is reinitialized on each iteration. You simply need to remove the for (int i = 0, n = strlen(key); i < n; i++) loop altogether, neither i or n is used. The same for ciphertext and l (ell).
You need to move int u = 64; immediately after if (isupper(plaintext[k])) so it is reset on each iteration.
I suspect all the printf statements were for debugging and you really only want the key output. Putting it altogether, you can rearrange your code to:
#include <cs50.h>
#include <stdio.h>
#include <ctype.h>
#include <string.h>
#include <stdlib.h>
int main (int argc, string argv[]) {
if (argc == 2) { /* validate key given */
string key = argv[1];
string plaintext = get_string("plaintext: ");
printf("ciphertext: ");
for (int k = 0, p = strlen(plaintext); k < p; k++)
{
if (isupper(plaintext[k]))
{
int u = 64;
for (int j = 0; j < 26; j++)
{
u = u + 1;
if (u == plaintext[k]) {
printf("%c", key[j]);
break;
}
}
}
else
printf("%c", plaintext[k]);
}
putchar ('\n');
}
}
Example Use/Output
$ ./bin/cs50_cypher2 JTREKYAVOGDXPSNCUIZLFBMWHQ
plaintext: ABD
ciphertext: JTE
Simplify Your Logic
If you think about what the code above is actually doing, it can be reduced to:
#include <cs50.h>
#include <stdio.h>
#include <ctype.h>
#include <string.h>
int main (int argc, string argv[]) {
if (argc < 2) { /* validate key given */
fputs ("usage: ./program key\n", stderr);
return 1;
}
string key = argv[1];
string plaintext = get_string("plaintext: ");
printf("ciphertext: ");
for (int k = 0, p = strlen(plaintext); k < p; k++)
{
if (isupper(plaintext[k]))
putchar (key[plaintext[k] - 'A']);
else
putchar (plaintext[k]);
}
putchar ('\n');
}
Or if you use a ternary operator, your for loop condenses to simply:
for (int k = 0, p = strlen(plaintext); k < p; k++)
putchar (isupper(plaintext[k]) ? key[plaintext[k] - 'A'] : plaintext[k]);
And since there is no need to call strlen(plaintext), you can eliminate string.h entirely and just loop for (int k = 0; plaintext[k]; k++) since plaintext is a nul-terminated string, your entire program can reduce to:
#include <cs50.h>
#include <stdio.h>
#include <ctype.h>
int main (int argc, string argv[]) {
if (argc < 2) { /* validate key given */
fputs ("usage: ./program key\n", stderr);
return 1;
}
string key = argv[1];
string plaintext = get_string("plaintext : ");
printf("ciphertext: ");
for (int k = 0; plaintext[k]; k++)
putchar (isupper(plaintext[k]) ? key[plaintext[k] - 'A'] : plaintext[k]);
putchar ('\n');
}
Handling Lowercase Letters
You know your key entered as the first argument is all uppercase. So if you want to output a lowercase key, you must call tolower() on the key after you have applied the offsets. Since for an uppercase letter, you just want to know the offset within the 26-character key to use, you simply find out how many letters your current letter is from 'A' and get that offset in key[], e.g.
key[plaintext[k] - 'A']
For lowercase input, you need to find out the offset in key for the difference in lowercase letters, and then apply tolower() to the key, e.g.
tolower(key[plaintext[k] - 'a'])
See ASCII Table & Description
Putting that altogether, your for loop can be written as:
for (int k = 0; plaintext[k]; k++) {
if (isupper(plaintext[k]))
putchar (key[plaintext[k] - 'A']);
else if (islower(plaintext[k]))
putchar (tolower(key[plaintext[k] - 'a']));
else
putchar (plaintext[k]);
}
Example Use/Output
$ ./bin/cs50_cypher4 JTREKYAVOGDXPSNCUIZLFBMWHQ
plaintext : AbZ 50
ciphertext: JtQ 50
Look things over and let me know if you have further questions.

Wrapping Text Using Modulo

I am trying to cipher some plain text using a string key. Anyway if the plain text is greater than the key the key is suppose to continue using the key..I have used modulo to make the key start over again but for some reason it is not working...what is wrong with the code? By the way the key status upper or lower status is not a factor so this is why I change it to lower. Any help rendered would be appreciated. //Code cleaned up as suggested.
#include <stdio.h>
#include <cs50.h>
#include <string.h>
#include<stdlib.h>
#include<ctype.h>
int main (int argc, string argv[])
{
if (argc != 2)
{
printf("Ouch missing key\n");
return 1;
}
//get encryption keyword from argv array
string k= (argv[1]);
//test for non aplha character in plain text message
int x;
for (x = 0; x <strlen(argv[1]); x++)
{
if(isalpha(k[x]) == false)
{
printf("Ouch ensure value is alphabetical only\n");
return 1;
}
}
string m;
m = GetString(); //get plain text from prompt
for (int i= 0, j = 0; i< strlen(m) && j<= strlen(k); i++, j++)
{
if (
isalpha(m[i]) && isupper(m[i]))
{
m[i]= (m[i]-'a' + (tolower(k[j % strlen(k)])-'a')) % 26 + 'A';
}
else if (
isalpha(m[i]) && islower(m[i])
)
{
m[i] = (m[i] - 'a' + (tolower(k[j %strlen(k)])- 'a')) % 26 + 'a';
}
else
m[i] = m[i];
}
printf("%s\n", m);
}
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <ctype.h>
#include <cs50.h>
int main (int argc, string argv[]){
if (argc != 2){
printf("Ouch missing key\n");
return 1;
}
string k = argv[1];
int len_k = strlen(k);
for (int i = 0; i < len_k; ++i){
if(isalpha(k[i]))
k[i] = tolower(k[i]);
else {
printf("Ouch ensure value is alphabetical only\n");
return 1;
}
}
string m;
m = GetString();
for (int i= 0; i< m[i] ; ++i){
if(isupper(m[i]))
m[i]= (m[i]-'A' + k[i % len_k] - 'a') % 26 + 'A';
else if(islower(m[i]))
m[i]= (m[i]-'a' + k[i % len_k] - 'a') % 26 + 'a';
else
m[i] = m[i];//no effect, no need
}
printf("%s\n", m);
free(m);
return 0;
}
If your coding language is C fix main() by,
int main (int argc, string argv[])
to
int main (int argc, char * argv[])

Pattern matching in long text string

I wrote this code and it works in some cases. Sometimes, however, it fails, and I just can't see why. Can someone please help me spot the error?
It works for:
String: ishanthakkar ishan
patter: ishan
But it fails for:
String: cpr ograming
patter: cpr
Source:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int *compute_prefix_function(char *pattern, int psize)
{
int k = -1;
int i = 1;
int *pi = malloc(sizeof(int)*psize);
if (!pi)
return NULL;
pi[0] = k;
for (i = 1; i < psize; i++) {
while (k > -1 && pattern[k+1] != pattern[i])
k = pi[k];
if (pattern[i] == pattern[k+1])
k++;
pi[i] = k;
}
return pi;
}
// This function find matching string in O(n) time, so iterate through text string only once, when unmatching character found; it proceed with next character and start comparing with first character of string to be searched i.e pattern
int kmp(char *target, int tsize, char *pattern, int psize)
{
int i;
int *pi = compute_prefix_function(pattern, psize);
int k = -1;
if (!pi)
return -1;
for (i = 0; i < tsize; i++) {
while (k > -1 && pattern[k+1] != target[i])
k = pi[k];
if (target[i] == pattern[k+1])
k++;
if (k == psize - 1) {
free(pi);
return i-k;
}
}
free(pi);
return -1;
}
int main(int argc, const char *argv[])
{
char target[200];
char *ch = target;
char pattern[20];
int i;
printf("Enter the string: \n");
fgets(target,100,stdin);
printf("Enter the string to be matched: \n");
fgets(pattern,20,stdin);
i = kmp(target, strlen(target), pattern, strlen(pattern));
if (i >= 0)
printf("matched #: %s\n", ch + i);
getch();
return 0;
}
The fgets function reads and includes the ending CR (or CRLF) in the string.
Add a chomp() function, like
void chomp(char *s) {
int n = strlen(s);
while (n && (s[n-1]==10 || s[n-1]==13)) s[--n] = 0;
}
that removes any CR or LF at the end of the string.
Then chomp() pattern and target before calling kmp() (and after the scanf())
chomp(target);
chomp(pattern);
i = kmp(target, strlen(target), pattern, strlen(pattern));
the program should behave better.
Note: 10 is '\n' (LF) and 13 is '\r' (CR)
Got clue:
i = kmp(target, strlen(target), pattern, strlen(pattern));
was passing string length+1(for null character) so it was giving false result for some text string
i = kmp(target, strlen(target)-1, pattern, strlen(pattern)-1);
works for all cases!
Thanks to all of you for your time!

Resources