I'm attempting to create a very simple encryption where a string is scanned, and the ascii code is increased by 5 (+5). When the letters reach the end of the alphabet it wraps back around to the beginning (same with numbers). punctuation and any other symbols are not encrypted. The encryption function seems to be working, however I'm having trouble passing the string back to the main function. What has gone wrong?
Here is my code:
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
/*function definitions */
char encrypt(char input_string[]);
int main(void)
{
char input_string[25];
char new_string[25];
char encrypted_string[25];
int i = 0;
// scan word
printf("Enter word: ");
scanf(" %[^\n]", input_string);
fflush(stdin);
//check input
printf("%s\n", input_string);
// call encrypt function
encrypted_string[25] = encrypt(input_string);
// print encrypted version
printf("encrypted: %s\n\n", encrypted_string);
return 0;
}
char encrypt(char input_string[])
{
char new_string;
static char str[25];
int i;
for(i = 0; i < 25; i++)
{
if (input_string[i] > 96 && input_string[i] < 118) // for a - u
{
new_string = (input_string[i] + 5);
}
else if (input_string[i] > 117 && input_string[i] < 123) // for v - z
{
new_string = (input_string[i] - 21);
}
else if (input_string[i] > 64 && input_string[i] < 86) // for A - U
{
new_string = (input_string[i] + 5);
}
else if (input_string[i] > 85 && input_string[i] < 91) // for V - Z
{
new_string = (input_string[i] - 21);
}
else if (input_string[i] > 47 && input_string[i] < 53) // for 0 - 4
{
new_string = (input_string[i] + 5);
}
else if (input_string[i] > 52 && input_string[i] < 58) // for 5 - 9
{
new_string = (input_string[i] - 5);
}
else
{
new_string = input_string[i];
}
str[i] = new_string;
}
printf("\n\n");
i=0;
//check asccii codes
while(str[i])
{
printf("%d ", str[i++]);
}
//print string
printf("\n\n%s\n\n", str);
//return string to main
return(str[25]);
}
return(str[25]); means you're trying to return the value of the 26th element,[which is out-of-bound access], not the address of the string.
encrypted_string[25] = encrypt(input_string);
No, you cannot assign a string return like that. It's wrong in many ways.
To correct, follow the below steps.
change char encrypt(char input_string[]) to char* encrypt(char input_string[])
use return(str); instead of return(str[25]);
instead of char encrypted_string[25];, write char * encrypted_string;
NOTE:
using static array will do the job, but IMO, a better approach will be allocating the array dynamically and return the pointer after populating from encrypt() function. You can later free() the pointer from main().
Your function:
char encrypt(char input_string[])
literally says that it accepts a long string as input, and returns just a single character as output.
That seems very dubious.
Don't you want it to return a string as output, not just one letter?
Related
This lab is trying to show the use of coder-defined functions to execute the code, but I'm trying to do it alternatively so when we actually are tested on it I won't be freaking out that I just copied the source code.
#define NotFound -1
#define WordSize 20
int stringSearch(char * string, char array, int * letter);
int main(void)
{
char * string = (char *) malloc(WordSize * sizeof(char));
char tester = '\0';
int index_tester = 0, i;
// do
// {
// printf("Enter a test string and character, enter q for the test string to exit.\n");
// printf("Test string: ");
// scanf("%s", string);
// while (getchar() != '\n') {}
// if (strcmp(string, "q") == 0) {
// break;
// }
// } // ----> Is it possible to do a while or for look instead? loop here?
printf("What is the test string you wish to enter: ?");
for (i = 0; i < sizeof(string); i++)
{
{
scanf("%c", &string[i]);
}
}
string[i] = '\0';
puts(string);
printf("Tester for the inputed string: ");
scanf("%c", &tester);
while (getchar() != '\n') {}
int ResultofSearch = stringSearch(string, tester, &index_tester);
if (ResultofSearch == NotFound)
{
printf("That letter is not foudn in the string, try again: ");
}
else {
printf("Character found at index %d.\n\n", index_tester);
}
return 0;
}
int stringSearch(char * string, char array, int * letter)
{
for (int i = 0; i < strlen(string); i++)
{
if (string[i] == array)
{
*letter = i;
return (Found);
}
}
return(NotFound);
}
When executing the code, I can put in the string, which I think is working fine, but it will automatically put in the search for some random letters immediately without prompting for the user input. I'm still a greenhorn to all this coding stuff so sorry in advance, any advice would be appreciated though
Apart from the issues pointed out in the comments there is some things you should improve:
char * string = (char *) malloc(WordSize * sizeof(char)); is the same as char * string = malloc(WordSize), but for a 20 word string we will need char * string = malloc(WordSize + 1)
This part of the code:
for (i = 0; i < Wordsize; i++) // already corrected
{
{
scanf("%c", &string[i]);
}
}
string[i] = '\0';
This will obligate you to always have a 19 character string. The cycle will not end until you do (you replace the 20th character with the null-terminator).
You can replace the whole thing with:
fgets(string, WordSize + 1, stdin);
And for good measure, discard the extra characters when the input is too big to fit the string.
int c;
while((c = fgetc(stdin)) !='\n' && c =! EOF); //discard until newline, for completion check for EOF return
This will allow a 20 character max size string but also for smaller ones.
Working sample
You should add required headers, use fgets() rather than scanf() and set tester_index to -1 rather than 0 which means found at index 0.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define Found 1
#define NotFound 0
#define WordSize 20
int stringSearch(char * string, char array, int * letter);
int main(void)
{
char * string = (char *) malloc(WordSize * sizeof(char));
//char tester = '\0';
char tester[2] = {'\0', '\0'};
int index_tester = -1; // 0 means found # index 0
/* (...) */
printf("What is the test string you wish to enter: ?\n");
fgets(string, WordSize, stdin);
if (string[WordSize-1]=='\n')
string[WordSize-1]='\0';
puts(string);
printf("Tester for the inputed string: \n");
while (getchar() != '\n') {}
///scanf("%c", &tester[0]);
fgets(tester, 2, stdin);
int ResultofSearch = stringSearch(string, tester[0], &index_tester);
if (ResultofSearch == NotFound)
{
printf("That letter is not found in the string.\n");
}
else {
printf("Character found at index %d.\n\n", index_tester);
}
return 0;
}
int stringSearch(char * string, char c, int * index)
{ ... }
It's definitely not perfect but works more less expected way.
I am working on Caesar's cipher for an online course and I have a problem with the original value being in the final output, and I cannot seem to get rid of it. My suspicion is that it is due to
strcpy(str1, &final_val);
strcat(str2, str1);
being called in a wrong way, so when I run
make test && ./test 1
This provides my program with the argument 1 and provides the key to shift the letters and encode the message. I expect to see
plaintext: asd
ciphertext: bcd
Instead I get
plaintext: asd
ciphertext: bacbdc
If you want to try out the code, you will need to do it inside of this sanbox, because it has the required CS50 library.
Code
#include <cs50.h>
#include <stdio.h>
#include <string.h>
#include <ctype.h>
bool input_valid(int count, string arg);
bool in_alphabet(int count, string arg);
int main(int argc, string argv[]) {
int key;
int ascii_val;
char final_val;
string string;
char str1[80];
char str2[80];
// check input again if validation fails
if (!input_valid(argc, argv[1])) {
printf("Invalid input!\nUSAGE: ./caesar key\n");
return 1;
}
string = get_string("plaintext: ");
// get integer from string input
key = strtol(argv[1], NULL, 10);
for (int i = 0; i < strlen(string); i++) {
ascii_val = (int)string[i];
bool valid_lower_case = (ascii_val + key) >= 97 && (ascii_val + key) <= 122;
bool valid_upper_case = (ascii_val + key) >= 65 && (ascii_val + key) <= 90;
// check if value is a letter
if (isalpha(string[i])) {
// check if value is in the valid alphabet range
if (valid_lower_case || valid_upper_case) {
final_val = ascii_val + key;
} else {
// for lowercase: wrap around if the letter passes 'z'
final_val = 97 + (key - (122 - (ascii_val - 1)));
}
} else {
final_val = ascii_val;
}
strcpy(str1, &final_val);
strcat(str2, str1);
}
for (int i = 0; i < 5; i++) {
printf("%i\n", str2[i]);
}
printf("ciphertext: %s\n", str2);
}
bool input_valid(int count, string arg) {
// input has more args than just the file name
// input is an integer
return count > 1 && isdigit(arg[0]);
}
strcpy(str1, &final_val); is undefined behavior. strcpy expects both parameters to be pointers to null-terminated strings. However, since arrays decay to pointers when passed to functions, strcpy doesn't know the difference between a pointer to an array of characters and the address of a single char variable.
It will try to copy memory beginning at &final_val into str1, only stopping when it encounters a null terminator elsewhere in your process' memory, if there is one. To copy a single character to a string, just use str[x] = ch and str[x + 1] = '\0'.
Replaced strcopy() and strcat(). Got it to work by appending to str2 like this:
str2[i] = final_val;
This is for Homework
I have to write a program that asks the user to enter a string, then my program would separate the even and odd values from the entered string. Here is my program.
#include <stdio.h>
#include <string.h>
int main(void) {
char *str[41];
char odd[21];
char even[21];
int i = 0;
int j = 0;
int k = 0;
printf("Enter a string (40 characters maximum): ");
scanf("%s", &str);
while (&str[i] < 41) {
if (i % 2 == 0) {
odd[j++] = *str[i];
} else {
even[k++] = *str[i];
}
i++;
}
printf("The even string is:%s\n ", even);
printf("The odd string is:%s\n ", odd);
return 0;
}
When I try and compile my program I get two warnings:
For my scanf I get "format '%s' expects arguments of type char but argument has 'char * (*)[41]". I'm not sure what this means but I assume it's because of the array initialization.
On the while loop it gives me the warning that says comparison between pointer and integer. I'm not sure what that means either and I thought it was legal in C to make that comparison.
When I compile the program, I get random characters for both the even and odd string.
Any help would be appreciated!
this declaration is wrong:
char *str[41];
you're declaring 41 uninitialized strings. You want:
char str[41];
then, scanf("%40s" , str);, no & and limit the input size (safety)
then the loop (where your while (str[i]<41) is wrong, it probably ends at once since letters start at 65 (ascii code for "A"). You wanted to test i against 41 but test str[i] against \0 instead, else you get all the garbage after nul-termination char in one of odd or even strings if the string is not exactly 40 bytes long)
while (str[i]) {
if (i % 2 == 0) {
odd[j++] = str[i];
} else {
even[k++] = str[i];
}
i++;
}
if you want to use a pointer (assignement requirement), just define str as before:
char str[41];
scan the input value on it as indicated above, then point on it:
char *p = str;
And now that you defined a pointer on a buffer, if you're required to use deference instead of index access you can do:
while (*p) { // test end of string termination
if (i % 2 == 0) { // if ((p-str) % 2 == 0) { would allow to get rid of i
odd[j++] = *p;
} else {
even[k++] = *p;
}
p++;
i++;
}
(we have to increase i for the even/odd test, or we would have to test p-str evenness)
aaaand last classical mistake (thanks to last-minute comments), even & odd aren't null terminated so the risk of getting garbage at the end when printing them, you need:
even[k] = odd[j] = '\0';
(as another answer states, check the concept of even & odd, the expected result may be the other way round)
There are multiple problems in your code:
You define an array of pointers char *str[41], not an array of char.
You should pass the array to scanf instead of its address: When passed to a function, an array decays into a pointer to its first element.
You should limit the number of characters read by scanf.
You should iterate until the end of the string, not on all elements of the array, especially with (&str[i] < 41) that compares the address of the ith element with the value 41, which is meaningless. The end of the string is the null terminator which can be tested with (str[i] != '\0').
You should read the characters from str with str[i].
You should null terminate the even and odd arrays.
Here is a modified version:
#include <stdio.h>
int main(void) {
char str[41];
char odd[21];
char even[21];
int i = 0;
int j = 0;
int k = 0;
printf("Enter a string (40 characters maximum): ");
if (scanf("%40s", str) != 1)
return 1;
while (str[i] != '\0') {
if (i % 2 == 0) {
odd[j++] = str[i];
} else {
even[k++] = str[i];
}
i++;
}
odd[j] = even[k] = '\0';
printf("The even string is: %s\n", even);
printf("The odd string is: %s\n", odd);
return 0;
}
Note that your interpretation of even and odd characters assumes 1-based offsets, ie: the first character is an odd character. This is not consistent with the C approach where an even characters would be interpreted as having en even offset from the beginning of the string, starting at 0.
Many answers all ready point out the original code`s problems.
Below are some ideas to reduce memory usage as the 2 arrays odd[], even[] are not needed.
As the "even" characters are seen, print them out.
As the "odd" characters are seen, move them to the first part of the array.
Alternative print: If code used "%.*s", the array does not need a null character termination.
#include <stdio.h>
#include <string.h>
int main(void) {
char str[41];
printf("Enter a string (40 characters maximum): ");
fflush(stdout);
if (scanf("%40s", str) == 1) {
int i;
printf("The even string is:");
for (i = 0; str[i]; i++) {
if (i % 2 == 0) {
str[i / 2] = str[i]; // copy character to an earlier part of `str[]`
} else {
putchar(str[i]);
}
}
printf("\n");
printf("The odd string is:%.*s\n ", (i + 1) / 2, str);
}
return 0;
}
or simply
printf("The even string is:");
for (int i = 0; str[i]; i++) {
if (i % 2 != 0) {
putchar(str[i]);
}
}
printf("\n");
printf("The odd string is:");
for (int i = 0; str[i]; i++) {
if (i % 2 == 0) {
putchar(str[i]);
}
}
printf("\n");
here is your solution :)
#include <stdio.h>
#include <string.h>
int main(void)
{
char str[41];
char odd[21];
char even[21];
int i = 0;
int j = 0;
int k = 0;
printf("Enter a string (40 characters maximum): ");
scanf("%s" , str);
while (i < strlen(str))
{
if (i % 2 == 0) {
odd[j++] = str[i];
} else {
even[k++] = str[i];
}
i++;
}
odd[j] = '\0';
even[k] = '\0';
printf("The even string is:%s\n " , even);
printf("The odd string is:%s\n " , odd);
return 0;
}
solved the mistake in the declaration, the scanning string value, condition of the while loop and assignment of element of array. :)
Reposting because my first post was no good. I have a question that I'm not really sure how to do. I know the process I'm going for, but am not totally sure how to scan a string into an array so that each character/integer is scanned into a independent element of the array. I'll post the question and the code I have so far, and any help would be appreciated.
Question:
Assume that we have a pattern like the following: ([n][letter])+ in which n is an integer number and letter is one of the lowercase letters from a-z. For example, 2a and 3b are valid expressions based on our pattern. Also, “+” at the end of the pattern means that we have at least one expression (string) or more than one expression attached. For instance, 2a4b is another valid expression which is matched with the pattern. In this question, we want to convert these valid expressions to a string in which letters are repeated n times.
o Read an expression (string) from user and print the converted version of the expression in the output.
o Check if input expression is valid. For example, 2ab is not a valid expression. If the expression is not valid, print “Invalid” in the output and ask user to enteranother expression.
o Sample input1 = “2a”, output = aa
o Sample input2 = “2a3b”, output = aabbb
o You will receive extra credit if you briefly explain what concept or theory you can use to check whether an expression is valid or not.
What I have so far:
#include <stdio.h>
int main()
{
int size, i, j;
char pattern[20];
char vowel[20];
int count[20];
printf("Please enter your string: ");
gets(pattern);
size = strlen(pattern);
for(i=0; i<size; i++)
if((i+1)%2 == 0)
vowel[i] = pattern[i];
else if((i+1)%2 != 0)
count[i] = pattern[i];
for(i=0; i<size/2; i++);
for(j=0; j<count[i]; j++)
printf("%s", vowel[i]);
}
I assumed you want to write the "invalid\n" string on stderr. If not just change the file descriptor given to write.
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#define MAX_INPUT_SIZE 20
int
check_input(char *input)
{
while (*input)
{
if (*input < '0' || *input > '9')
{
write(2, "invalid\n", 8);
return 1;
}
while (*input >= '0' && *input <= '9')
input++;
if (*input < 'a' || *input > 'z')
{
write(2, "invalid\n", 8);
return 1;
}
input++;
}
return 0;
}
void
print_output(char *input)
{
int i;
while (*input)
{
i = atoi(input);
while (*input >= '0' && *input <= '9')
input++;
for (; i > 0; i--)
write(1, input, 1);
input++;
}
write(1, "\n", 1);
}
int
main()
{
char input[MAX_INPUT_SIZE];
do
{
printf("Please enter your string: ");
fgets(input, MAX_INPUT_SIZE, stdin);
input[strlen(input) - 1] = '\0';
}
while (check_input(input));
print_output(input);
return 0;
}
The steps are:
Read pattern
Check if pattern is valid
Generate output
Since the input length is not specified you have to assume a maximum length.
Another assumption is n is a single digit number.
Now you may read the whole expression with fgets() or read it char by char.
The latter allows you to check for validity as you read.
Lets use fgets() for convenience and in case the expression needs to be stored for later use.
char exp[100]; // assuming at most 50 instances of ([n][letter])
int len;
printf("Input: ");
fgets(exp, 100, stdin);
len = strlen(exp) - 1; // Discard newline at end
An empty input is invalid. Also a valid expression length should be even.
if (len == 0 || len%2 != 0) {
printf("Invalid-len\n");
return 1;
}
Now parse the expression and separately store numbers and letters in two arrays.
char nums[50], letters[50];
invalid = 0;
for (i = 0, j = 0; i < len; i += 2, j++) {
if (exp[i] >= '1' && exp[i] <= '9') {
nums[j] = exp[i] - '0';
} else {
invalid = 1;
break;
}
if (exp[i+1] >= 'a' && exp[i+1] <= 'z') {
letters[j] = exp[i+1];
} else {
invalid = 1;
break;
}
}
Notice that in each iteration if first char is not a number or second char is not a letter, then the expression is considered to be invalid.
If the expression is found to be invalid, nothing to do.
if (invalid) {
printf("Invalid\n");
return 1;
}
For a valid expression run nested loops to print the output.
The outer loop iterates for each ([n][letter]) pattern.
The inner loop prints n times the letter.
printf("Output: ");
for (i = 0; i < len/2; i++) {
for (j = 0; j < nums[i]; j++)
printf("%c", letters[i]);
}
This is a rather naive way to solve problems of this type. It is better to use regular expressions.
C standard library doesn't have regex support. However on Unix-like systems you can use POSIX regular expressions.
like this
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include <stdbool.h>
#define prompt "Please enter your string: "
void occurs_error(const char *src, const char *curr){
printf("\nInvalid\n");
printf("%s\n", src);
while(src++ != curr){
putchar(' ');
}
printf("^\n");
}
bool invalid(char *pattern){
char *p = pattern;
while(*p){
if(!isdigit((unsigned char)*p)){//no number
occurs_error(pattern, p);
break;
}
strtoul(p, &p, 10);
if(!*p || !islower((unsigned char)*p)){//no character or not lowercase
occurs_error(pattern, p);
break;
}
++p;
}
return *p;
}
int main(void){
char pattern[20];
while(fputs(prompt, stdout), fflush(stdout), fgets(pattern, sizeof pattern, stdin)){
pattern[strcspn(pattern, "\n")] = 0;//chomp newline
char *p = pattern;
if(invalid(p)){
continue;
}
while(*p){
int n = strtoul(p, &p, 10);
while(n--)
putchar(*p);
++p;
}
puts("");
}
}
Using only stdio.h, string.h and stdlib.h libraries how would I go about implementing this?
I'm quite new to programming so please bear with me!
Allocate a new char array of the same length as your string. Convince yourself that this is enough space. Don't forget the NUL.
Loop through the string, copying to the new string only those characters that are alphanumeric. You can't do this portably without also including <ctype.h> and using a function/macro from that header, unless you're going to enumerate all characters that you consider alphanumeric.
Again, don't forget the NUL.
Since this is homework, here is the verbal description:
Run a loop over the original string and use the functions isalnum() to determine if a character is alphanumeric. Maintain a second char array of reasonable size and every time you encounter an AlphaNum, insert it to that array. Once all AlphaNum characters have been copied to the second array, NULL terminate it and you have your string.
Note: isalnum() is defined in ctype.h, so if you aren't allowed to use that, you may have to define this function for yourself. That is another exercise of it's own.
Every char you read in your string is a byte (you can think it as a number between 0 and 255, and that's how the computers handle them) so you just need to check the ascii table to see what letter refers to.
Every alphanumerical char is in this range: [48, 58] (for numbers), or [65, 90] (upper case), or [97, 122] (lower case).
Look at this:
#include <stdio.h>
#include <stdlib.h>
#define SIZE 64
int isalphanum(char); /*states if a char is alphanumerical or not*/
char *getalphanum(char *, char*); /*modifies the second string to get the result*/
int main(void) {
char in[SIZE] = "Hello, W##########orl...,.,d!"; /*just a random to try out*/
char out[SIZE];
getalphanum(in, out);
printf("%s", out);
return 0;
}
int isalphanum(char a) {
if ((a >= 48) && (a <= 58))
return 1;
if ((a >= 65) && (a <= 90))
return 1;
if ((a >= 97) && (a <= 122))
return 1;
return 0;
}
char *getalphanum(char *s, char *t) {
if ((s == NULL) || (t == NULL)) /*tests if the strings are "handble"*/
return NULL;
int i = 0;
int j = 0;
char c;
while ((c = *(s + i)) != '\0') {
if (isalphanum(c)){
*(t + j) = c;
j++;
}
i++;
}
*(t + j) = '\0';
return t;
}
This code works and is very simple and can be improved, but there is evertything you need.
The best way is to use the isalnum() from ctype.h but now that is not an option, I have written a not-standard/non-portable function called isalnum_not_prefered() which is the equivalent of ctype.h's isalnum().
#include <stdio.h>
#include <ctype.h>
#include <string.h>
#include <stdlib.h>
int isalnum_not_prefered(char s)
{
if((s >= 'A' && s <= 'Z') ||
(s >= 'a' && s <= 'z') ||
(s >= '0' && s <= '9'))
return 1;
return 0;
}
int main(void)
{
char str[] = "this!234$#&##$^is5##$a##$4677~=_?}valid2234kjstring";
int len = strlen(str);
int i, j=0;
char *newstr1 = NULL;
char *newstr2 = NULL;
if ((newstr1 = (char *) malloc(sizeof(char) * len + 1)) == NULL) {
printf("unable to allocate memory \n");
return -1;
}
for (i=0 ; i<len ; i++) {
if (isalnum(str[i])) {
newstr1[j] = str[i];
j++;
}
}
newstr1[j] = '\0';
if ((newstr2 = (char *) malloc(sizeof(char) * len + 1)) == NULL) {
printf("unable to allocate memory \n");
return -1;
}
j=0;
for (i=0 ; i<len ; i++) {
if (isalnum_not_prefered(str[i])) {
newstr2[j] = str[i];
j++;
}
}
newstr2[j] = '\0';
printf("string : %s \n", str);
printf("result1 : %s \n", newstr1);
printf("result2 : %s \n", newstr2);
free(newstr1);
free(newstr2);
return 0;
}
Points to note:
strings in C is terminated with \0. So the new string that your are populating should also terminate with \0
malloc()'ed memory must be free()'ed
malloc() errors should be handled
this code is not portable as it assumes the machines character set to be ASCII. If the hardware supports some other character set (say EBCDIC) then this may not work as expected.
Hope this helps!