I'm trying to create a program in C that prints the letters from alphabet that are not in gussed_character. All unused letters are saved in available_character.
Code:
#include <stdio.h>
#include <ctype.h>
#include <string.h>
void letters(const char gussed_character[], char available_character[]){
char alphabet[] = "abcdefghijklmnopqrstuvwxyz";
int gussed_character_lenght = strlen(gussed_character);
int alphabet_lenght = strlen(alphabet);
int counter = 0;
for(int i = 0; i<alphabet_lenght; i++){
counter = 0;
for(int j = 0; j<gussed_character_lenght; j++){
if(alphabet[i] != gussed_character[j]){
counter++;
}
if(counter == gussed_character_lenght){
available_character[i] = alphabet[i];
}
}
}
printf("%s", available_character);
}
int main(){
char result[30];
letters("arpstxgoieyu", result);
}
Example from code:
letters("arpstxgoieyu", result);
This letters are used and prgram should print this letters:
bcdfhjklmnqvwz
But my program print this:
%bcd#fPhajklmn q
Your bug is inside this snippet:
...
if(counter == gussed_character_lenght){
available_character[i] = alphabet[i];
}
...
When you determine that indeed, gussed_character contains alphabet[i], you try to add that character to the available_character array. However, you put alphabet[i] at position i in available_character. This means that the character inside alphabet at position i is copied to that same position in available_character. Notice that the output string contains the not used characters in the same position as in alphabet:
"a b c d e f g h i j k l m n o p q r s t u v w x y z" // alpabet
| | | | | | | | | |
V V V V V V V V V V
"% b c d # f P h a j k l m n q" // output string
Then the skipped characters are undefined, since you don't write anything to there (meaning it will take the value of whatever happens to be at that memory location at that time, making it random).
Instead of positioning the available character at position i, place it at the end of available_character. You can use a variable to keep track of the end of the available_character string at position alphabet[i] there.
I took your requirements and also implemented it myself, since I see some points where the code could also be improved (besides the bug):
#include <stdio.h>
#include <ctype.h>
#include <string.h>
#include <stdbool.h>
void letters(const char* used_characters, char* available_characters){
const char* alphabet = "abcdefghijklmnopqrstuvwxyz";
unsigned int available_character_index = 0;
for (const char* alphabet_letter = alphabet; *alphabet_letter != '\0'; alphabet_letter++) {
bool character_is_used = false;
for (const char* used_character = used_characters; *used_character != '\0'; used_character++) {
if (*used_character == *alphabet_letter) {
character_is_used = true;
break;
}
}
if (!character_is_used) {
available_characters[available_character_index++] = *alphabet_letter;
}
}
available_characters[available_character_index] = '\0';
printf("%s", available_characters);
}
int main(){
char result[30];
letters("arpstxgoieyu", result);
return 0;
}
Main takeaways are:
Obvious one is the bug fix. I now use a variable available_character_index to keep track of the end of the available_characters string and position any available characters there, also taking care to update available_character_index whenever I add a character.
You forgot to terminate the available_characters string. This could lead to the terminal going haywire when you print the available_characters string.
I think it's conceptually easier to iterate through a string by having a char pointer that starts at the beginning of the string and works on the array till it hits \0 (the null character). So, I changed the for-loops to work with them instead of an integer index.
I find your use of the counter variable a bit confusing. From what I gather, you're incrementing it only when alphabet[i] != gussed_character[j], so at the end if gussed_characters contains some character in aphabet, it should no longer be equal to strlen(gussed_characters). That condition does work, but it's quite confusing. In this case, a boolean is more appropriate (simple == better).
Related
Trying to do a Caesar cipher.
enum alfabeto{
A=0,B,C,D,E,F,G,H,I,L,M,N,O,P,Q,R,S,T,U,V,Z // 21
};
void cifra(char *string, int k){
enum alfabeto letter; // initialize the enum letter
size_t i = 0; // initialize counter
while (*(string+i)!='\0'){ // while string is not ended
letter = *(string+i); // attempt to "link" the enum letter to the equivalent (already uppercased) char
printf("%d", letter);
letter = (letter+k) % 21; // then it increases of a factor k and if it goes out of 21, it should take the right value
printf(" %d\n", letter);
++i;
}
}
Output:
$ ./"cesare"
write the text:
>TEST
choose the factor k:
>2
84 8
69 14
83 7
84 8
The values are wrong... maybe because I can't "link" a enum value to a char... How could I do this?c
letter = *(string+i); // attempt to "link" the enum letter to the equivalent (already uppercased) char
Should be:
letter = *(string+i) - 'A'; // attempt to "link" the enum letter to the equivalent (already uppercased) char
That way, 'A' will map to zero, as required.
// A B C D E F G H I J K L, M, N, O, P, Q, R, S, T, U, V, W, X, Y, Z
const int offsetTable[] = {0,1,2,3,4,5,6,7,8,-1,-1,9,10,11,12,13,14,15,16,17,18,19,-1,-1,-1,20};
const char charLookupTable[] = "ABCDEFGHILMNOPQRSTUVZ";
// Should probably use a struct for the above to prevent size mis-matches
const int offsetTableSize = 21;
void cifra(char *str, int k){
char letter;
int newLetterOffset;
printf("%s\n", str); // Show initial string.
while (letter = *str++){ // while string is not ended
const int oldLetterOffset = offsetTable[letter - 'A']; // Get the old letter's offset.
if(letter <= 'A' || letter >= 'Z' || oldLetterOffset == -1)
{
printf("\nBad character %c found - ending string processing\n", letter);
return;
}
newLetterOffset = (oldLetterOffset + k) % offsetTableSize; // Get the letter's position, add the global offset, and wrap to table size.
printf("%c", charLookupTable[newLetterOffset]); // Use that offset to read the character's value from the shortened alphabet.
}
printf("\n\n");
}
int main()
{
cifra("HELLO", 0);
cifra("HELLO", 1);
cifra("HELLo", 1);
cifra("YELLO", 1);
return 0;
}
Notice I needed 2 tables to do the work as we had to go into and out of your shortened character set. Typically, we would use a struct to hold those 2 arrays, but I kept it simple for this example. Also, the arrays do not need to be global, but I placed them there also to keep things simpler.
Notice I also changed your printf() values to use characters and strings to make it easier to read.
Finally, I added some error checking as you cannot trust a user to give you a good string. Many a security flaw or random crash has resulted from that.
I made a code and my target is to put spacewhere the input word was found in a sentence.
i neet to replece the small word with space
like:
Three witches watched three watches
tch
output:
Three wi es wa ed three wa es
I made this code:
#include<stdio.h>
#define S 8
#define B 50
void main() {
char small[S] = {"ol"};
char big[B] = {"my older gradmom see my older sister"};
int i = 0, j = 0;
for (i = 0; i < B; i++)
{
for(j=0;j<S;j++)
{
if(small[j]!=big[i])
{
j=0;
break;
}
if(small[j]=='\0')
{
while (i-(j-1)!=i)
{
i = i - j;
big[i] = '\n';
i++;
}
}
}
}
puts(big);
}
First of all, in your exemple you work with newline '\n' and not with space.
Consider this simple example:
#include<stdio.h>
#define S 8
#define B 50
void main() {
char small[S] = {"ol"};
char big[B] = {"my older gradmom see my older sister"};
int i = 0, j = 0;
int cpt = 0;
int smallSize = 0;
// loop to retrieve smallSize
for (i = 0; i < S; i++)
{
if (small[i] != '\0')
smallSize++;
}
// main loop
for (i = 0; i < B; i++)
{
// stop if we hit the end of the string
if (big[i] == '\0')
break;
// increment the cpt and small index while the content of big and small are equal
if (big[i] == small[j])
{
cpt++;
j++;
}
// we didn't found the full small word
else
{
j = 0;
cpt = 0;
}
// test if we found the full word, if yes replace char in big by space
if (cpt == smallSize)
{
for (int k = 0; k < smallSize; k++)
{
big[i-k] = ' ';
}
j = 0;
cpt = 0;
}
}
puts(big);
}
You need first to retrieve the real size of the small array.
Once done, next step is to look inside "big" if there is the word small inside. If we find it, then replace all those char by spaces.
If you want to replace the whole small word with a single space, then you'll need to adapt this example !
I hope this help !
A possible way is to use to pointers to the string, one for reading and one for writing. This will allow to replace an arbitrary number of chars (the ones from small) with a single space. And you do not really want to nest loops but une only one to process every char from big.
Last but not least, void main() should never be used except in stand alone environment (kernel or embedded development). Code could become:
#include <stdio.h>
#define S 8
#define B 50
int main() { // void main is deprecated...
char small[S] = {"ol"};
char big[B] = {"my older gradmom see my older sister"};
int i = 0, j = 0;
int k = 0; // pointer to written back big
for (i = 0; i < B; i++)
{
if (big[i] == 0) break; // do not process beyond end of string
if(small[j]!=big[i])
{
for(int l=0; l<j; l++) big[k++] = small[l]; // copy an eventual partial small
big[k++] = big[i]; // copy the incoming character
j=0; // reset pointer to small
continue;
}
else if(small[++j] == 0) // reached end of small
{
big[k++] = ' '; // replace chars from small with a single space
j = 0; // reset pointer to small
}
}
big[k] = '\0';
puts(big);
return 0;
}
or even better (no need for fixed sizes of strings):
#include <stdio.h>
int main() { // void main is deprecated...
char small[] = {"ol"};
char big[] = {"my older gradmom see my older sister"};
int i = 0, j = 0;
int k = 0; // pointer to written back big
for (i = 0; i < sizeof(big); i++)
{
if(small[j]!=big[i])
...
In C strings are terminated with a null character '\0'. Your code defines a somehow random number at the beginning (B and S) and iterates over that much characters instead of the exact number of characters, the strings actually contain. You can use the fact that the string is terminated by testing the content of the string in a while loop.
i = 0;
while (str[i]) {
...
i = i + 1;
}
If you prefer for loops you can write it also as a for loop.
for (i = 0; str[i]; i++) {
...
}
Your code does not move the contents of the remaining string to the left. If you replace two characters ol with one character , you have to move the remaining characters to the left by one character. Otherwise you would have a hole in the string.
#include <stdio.h>
int main() {
char small[] = "ol";
char big[] = "my older gradmom see my older sister";
int s; // index, which loops through the small string
int b; // index, which loops through the big string
int m; // index, which loops through the characters to be modified
// The following loops through the big string up to the terminating
// null character in the big string.
b = 0;
while (big[b]) {
// The following loops through the small string up to the
// terminating null character, if the character in the small
// string matches the corresponding character in the big string.
s = 0;
while (small[s] && big[b+s] == small[s]) {
// In case of a match, continue with the next character in the
// small string.
s = s + 1;
}
// If we are at the end of the small string, we found in the
// big string.
if (small[s] == '\0') {
// Now we have to modify the big string. The modification
// starts at the current position in the big string.
m = b;
// First we have to put the space at the current position in the
// big string.
big[m] = ' ';
// And next the rest of the big string has to be moved left. The
// rest of the big string starts, where the match has ended.
while (big[b+s]) {
m = m + 1;
big[m] = big[b+s];
s = s + 1;
}
// Finally the big string has to be terminated by a null
// character.
big[m+1] = '\0';
}
// Continue at next character in big string.
b = b + 1;
}
puts(big);
return 0;
}
My code for CS50 pset2 Vigenere cypher is as follows. I am new to C programming.
[ I edited the code once after I got some suggestions and this code(below) is my new edited code.]
When I run the code it produces infinite loop and also new encrypted text is not produced as it is supposed to be. Can I please get some suggestions and advice regarding the correction of my code ?
Thank you,
#include <stdio.h>
#include <cs50.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
int main(int argc, string argv[])
{
if (argc != 2) //if it is not rqual to 2, it gives an error message.
{
printf("Enter the valid input : \n");
return 1;
}
if (argc == 2) //if two commands are given then it proceeds to other step.
{
string k = argv[1];
string m = GetString();
int l = strlen(k);
int p = strlen(m);
for( int i = 0; i <= p ; i++ ) //it has to keep on rotating from 0 to len of string and back to zero and so on.
{
{
i = i % l;
}
if (isalpha(m[i]) && isalpha(k[i])) // it proceeds ahead only if the input given is an alphabet, if the input is sth other than alphabet it prints exactly as it is.
{
for(int t = 0; t <= p ; t++)
{
if(isupper(m[t])) // when is it capital letter.
{
printf("%c", ( m[t] - 65 + k[i]) % 26 + 65);
}
if(islower(m[t])) // when it is small letter.
{
printf("%c" , ( m[t] - 97 + k[i])% 26 + 97);
}
}
}
else //if it is not an alphabet it returns as it is.
{
printf("%c", m[i]);
}
}
}
printf("\n");
return 0;
}
Let's look at the error. It says that the parameter you gave there is not an array, while you are using it as an array. And that's right : p is an integer, and not an array :
int p = strlen(msg);
Using p[i] means that you want to access the element number i of your p array. But it is impossible to reach this value, because p is simply an integer variable, and not an array.
What you probably wanted to use as an array was one of your string parameters, key or msg. A string variable in CS50 is the equivalent of a char * variable in classic C, and is used as an array of characters.
So i was doing some coding exercice on Talentbuddy (for those who know), and i cant get why i cant finish this one.
The exercice is removing a substring from a string, given as input the string, the position P where beginning to remove characters and N the number of characters needed to be remove.
Here is what i've done :
#include <stdio.h>
#include <unistd.h>
void remove_substring(char *s, int p, int n)
{
int idx;
idx = -1;
while (s[++idx] != '\0')
write(1, &s[idx == p - 1 ? idx + n : idx], 1);
}
When the input is "abcdefghi", P = 9 and N = 1, the result given is "abcdefgh" exactly the same as the one i get with my function. But TalentBuddy keep saying me that my output is wrong and i dont thing he (talentbuddy) is wrong.
Maybe there is a blank space or something between the "h" and the '\0'.
But i cant figure it cause when i add another write(1, "END", 3) at the end it appears like "abcdefghEND".
If the question is exclusively for strings( NULL Terminated )
Why can't this be as simple as this, unless it is a homework.
void removesubstr( const char *string, const char *substring )
{
char *p = strstr(string, substring);
if(p)
{
strcpy(p,p+strlen(substring));
}
}
Your problem is that you write something for every original index, even if it should be suppressed. What you write looks like abcdefgh, but it is abcdefgh<nul>, where the terminal doesn't render the <nul>.
You are mixing two methods here. Either filter out the removed substring:
void remove_substring(char *s, int p, int n)
{
int i = 0;
p--; /* convert to C-style index */
while (s[i] != '\0') {
if (i < p || i >= p + n) putchar(s[i]);
i++;
}
}
or skip the substring by jumping over it:
void remove_substring(char *s, int p, int n)
{
int i = 0;
int l = strlen(s);
while (i < l) {
if (i + 1 == p) {
i += n;
} else {
putchar(s[i++]);
}
}
}
You're trying to do a bit of both.
(I've avoided the awkward combination of prefix increment and starting at minus 1. And I've used putchar instead of unistd's write. And the termination by length is so you don't inadvertently jump beyond the terminating <nul>.)
trying to write function that returns 1 if every letter in “word” appears in “s”.
for example:

containsLetters1("this_is_a_long_string","gas") returns 1
containsLetters1("this_is_a_longstring","gaz") returns 0
containsLetters1("hello","p") returns 0
Can't understand why its not right:
#include <stdio.h>
#include <string.h>
#define MAX_STRING 100
int containsLetters1(char *s, char *word)
{
int j,i, flag;
long len;
len=strlen(word);
for (i=0; i<=len; i++) {
flag=0;
for (j=0; j<MAX_STRING; j++) {
if (word==s) {
flag=1;
word++;
s++;
break;
}
s++;
}
if (flag==0) {
break;
}
}
return flag;
}
int main() {
char string1[MAX_STRING] , string2[MAX_STRING] ;
printf("Enter 2 strings for containsLetters1\n");
scanf ("%s %s", string1, string2);
printf("Return value from containsLetters1 is: %d\n",containsLetters1(string1,string2));
return 0;
Try these:
for (i=0; i < len; i++)... (use < instead of <=, since otherwise you would take one additional character);
if (word==s) should be if (*word==*s) (you compare characters stored at the pointed locations, not pointers);
Pointer s advances, but it should get back to the start of the word s, after reaching its end, i.e. s -= len after the for (j=...);
s++ after word++ is not needed, you advance the pointer by the same amount, whether or not you found a match;
flag should be initialized with 1 when declared.
Ah, that should be if(*word == *s) you need to use the indirection operator. Also as hackss said, the flag = 0; must be outside the first for() loop.
Unrelated but probably replace scanf with fgets or use scanf with length specifier For example
scanf("%99s",string1)
Things I can see wrong at first glance:
Your loop goes over MAX_STRING, it only needs to go over the length of s.
Your iteration should cover only the length of the string, but indexes start at 0 and not 1. for (i=0; i<=len; i++) is not correct.
You should also compare the contents of the pointer and not the pointers themselves. if(*word == *s)
The pointer advance logic is incorrect. Maybe treating the pointer as an array could simplify your logic.
Another unrelated point: A different algorithm is to hash the characters of string1 to a map, then check each character of the string2 and see if it is present in the map. If all characters are present then return 1 and when you encounter the first one that is not present then return 0. If you are only limited to using ASCII characters a hashing function is very easy. The longer your ASCII strings are the better the performance of the second approach.
Here is a one-liner solution, in keeping with Henry Spencer's Commandment 7 for C Programmers.
#include <string.h>
/*
* Does l contain every character that appears in r?
*
* Note degenerate cases: true if r is an empty string, even if l is empty.
*/
int contains(const char *l, const char *r)
{
return strspn(r, l) == strlen(r);
}
However, the problem statement is not about characters, but about letters. To solve the problem as literally given in the question, we must remove non-letters from the right string. For instance if r is the word error-prone, and l does not contain a hyphen, then the function returns 0, even if l contains every letter in r.
If we are allowed to modify the string r in place, then what we can do is replace every non-letter in the string with one of the letters that it does contain. (If it contains no letters, then we can just turn it into an empty string.)
void nuke_non_letters(char *r)
{
static const char *alpha =
"abcdefghijklmnopqrstuvwxyz"
"ABCDEFGHIJKLMNOPQRSTUVWXYZ";
while (*r) {
size_t letter_span = strspn(r, alpha);
size_t non_letter_span = strcspn(r + letter_span, alpha);
char replace = (letter_span != 0) ? *r : 0;
memset(r + letter_span, replace, non_letter_span);
r += letter_span + non_letter_span;
}
}
This also brings up another flaw: letters can be upper and lower case. If the right string is A, and the left one contains only a lower-case a, then we have failure.
One way to fix it is to filter the characters of both strings through tolower or toupper.
A third problem is that a letter is more than just the 26 letters of the English alphabet. A modern program should work with wide characters and recognize all Unicode letters as such so that it works in any language.
By the time we deal with all that, we may well surpass the length of some of the other answers.
Extending the idea in Rajiv's answer, you might build the character map incrementally, as in containsLetters2() below.
The containsLetters1() function is a simple brute force implementation using the standard string functions. If there are N characters in the string (haystack) and M in the word (needle), it has a worst-case performance of O(N*M) when the characters of the word being looked for only appear at the very end of the searched string. The strchr(needle, needle[i]) >= &needle[i] test is an optimization if there are likely to be repeated characters in the needle; if there won't be any repeats, it is a pessimization (but it can be removed and the code still works fine).
The containsLetters2() function searches through the string (haystack) at most once and searches through the word (needle) at most once, for a worst case performance of O(N+M).
#include <assert.h>
#include <stdio.h>
#include <string.h>
static int containsLetters1(char const *haystack, char const *needle)
{
for (int i = 0; needle[i] != '\0'; i++)
{
if (strchr(needle, needle[i]) >= &needle[i] &&
strchr(haystack, needle[i]) == 0)
return 0;
}
return 1;
}
static int containsLetters2(char const *haystack, char const *needle)
{
char map[256] = { 0 };
size_t j = 0;
for (int i = 0; needle[i] != '\0'; i++)
{
unsigned char c_needle = needle[i];
if (map[c_needle] == 0)
{
/* We don't know whether needle[i] is in the haystack yet */
unsigned char c_stack;
do
{
c_stack = haystack[j++];
if (c_stack == 0)
return 0;
map[c_stack] = 1;
} while (c_stack != c_needle);
}
}
return 1;
}
int main(void)
{
assert(containsLetters1("this_is_a_long_string","gagahats") == 1);
assert(containsLetters1("this_is_a_longstring","gaz") == 0);
assert(containsLetters1("hello","p") == 0);
assert(containsLetters2("this_is_a_long_string","gagahats") == 1);
assert(containsLetters2("this_is_a_longstring","gaz") == 0);
assert(containsLetters2("hello","p") == 0);
}
Since you can see the entire scope of the testing, this is not anything like thoroughly tested, but I believe it should work fine, regardless of how many repeats there are in the needle.