Selection of unique characters - c

Please, help with the code.
Requirement:
Write a function my_union that takes two strings and returns, without doubles, the characters that appear in either one of the strings.
Example:
Input: "zpadinton" && "paqefwtdjetyiytjneytjoeyjnejeyj"
Output: "zpadintoqefwjy"
My code:
#include <stdbool.h>
#include <stdio.h>
#include <string.h>
char *my_union(char *a, char *b) {
char *str;
// Algorithm for excluding nonunique characters from string a(given in
// parameters).
str[0] = a[0];
int k = 1;
str[k] = '\0';
for (int i = 1; a[i] != '\0'; i++) {
bool is = true;
for (int j = 0; str[j] != '\0'; j++) {
if (str[j] == a[i]) {
is = false;
break;
}
}
if (is) {
str[k] = a[i];
k++;
str[k] = '\0';
}
} // In this case we are excluding excess character 'n' from "zpadinton", so
// str is equal to "zpadinto".
// Algorithm for adding unique characters from array b(given in parameters)
// into str.
for (int i = 0; b[i] != '\0'; i++) {
bool is = true;
for (int j = 0; str[j] != '\0'; j++) {
if (str[j] == b[i]) {
is = false;
break;
}
}
if (is) {
strncat(str, &b[i], 1);
}
}
return str;
}
The first algorithm is almost identical with second, but it doesn't work(. Mb I messed up with memory, give some advice, pls.

If you mean, get the unique characters from two strings and store them into a new string, try this code ;
First, you must allocate a memory for str. In your code, str is not pointing allocated memory location, so you will probably get segmentation fault.
int contains(const char * str,char c)
{
for (int i = 0; i < strlen(str); ++i)
if(str[i] == c)
return 1;
return 0;
}
char * my_union(char *a, char*b)
{
char * res = (char*)malloc(sizeof(char)*(strlen(a) + strlen(b)));
int pushed = 0;
for (int i = 0; i < strlen(a); ++i)
{
if(!contains(res,a[i])){
res[pushed] = a[i];
pushed++;
}
}
for (int i = 0; i < strlen(b); ++i)
{
if(!contains(res,b[i])){
res[pushed] = b[i];
pushed++;
}
}
return res;
}
int main(int argc, char const *argv[])
{
char string1[9] = "abcdefgh";
char string2[9] = "abegzygj";
char * result = my_union(string1,string2);
printf("%s\n", result);
return 0;
}
Also, do not forget the free the return value of my_union after you done with it.

Related

Segfault error when mallocing an array of structs

after a long time spent trying to debug this I've come for your help.
Basically in this exercise I'm trying to read the string "31|Name1;23|Name2;15|Name3" and store it in an array of struct s_perso where the | are marking the end of an age and the beginning of a name, and where the ; are marking the beginning of a new struct.
Here's the given ft_perso.h :
#include <string.h>
#ifndef FT__PERSO__H
#define FT__PERSO__H
typedef struct s_perso
{
char *name;
float life;
int age;
char *profession;
}
t_perso;
#endif
We will only use the datas age and name from this struct s_perso.
Here's my code :
#include "ft_perso.h"
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
int numberofstructs(char *str)
{
int i;
int length;
i = 0;
length = 0;
if (str[0])
length = 0;
else
{
while (str[i])
{
if (str[i] == ';')
length += 1;
i++;
}
}
return (length);
}
int get_data_length(char *str, int i)
{
int length;
length = 0;
while (str[i] != '|' && str[i] != ';' && str[i] != '\0')
{
length++;
i++;
}
return (length);
}
char *get_data(char *str, int i)
{
int j;
char *str2;
j = 0;
str2 = (char *)malloc(sizeof(char) * get_data_length(str, i) + 1);
while (str[i] != '|' && str[i] != ';' && str[i] != '\0')
{
str2[j] = str[i];
i++;
j++;
}
str2[j] = '\0';
return (str2);
}
t_perso **ft_decrypt(char *str)
{
int i;
int j;
t_perso **textttt_perso;
i = 0;
j = 0;
textttt_perso = (t_perso **)malloc(sizeof(t_perso **));
*textttt_perso = (t_perso *)malloc(sizeof(t_perso *) * numberofstructs(str));
while (j <= strlen(str) && str[j])
{
if (str[j] == ';')
{
i++;
j++;
}
textttt_perso[i]->age = atoi(get_data(str, j));
j = j + get_data_length(str, j) + 1;
textttt_perso[i]->name = get_data(str, j);
j = j + get_data_length(str, j);
}
textttt_perso[i+1] = 0;
return (textttt_perso);
}
int main(void)
{
int i;
t_perso **tab;
i = 0;
char str[29] = "31|Name1;23|Name2;15|Name3";
tab = ft_decrypt(str);
while(i <= numberofstructs(str))
{
printf("age = %d\n", tab[i]->age);
printf("age = %s\n", tab[i]->.name);
i++;
}
}
From my debugging, I get the segfault error on the second call (when i = 1 and we are working on the substring 23) instruction of t_perso **ft_decrypt(char *str) :
textttt_perso[i]->age = atoi(get_data(str, j));
My guess is that my allocation of memory either for the array of struct in itself or the number of arrays it can contain is wrong. I can't point my finger on the problem tho...
Thanks in advance for your help, have a nice day !
You never allocate space for an actual structure. In your example:
textttt_perso = (t_perso **)malloc(sizeof(t_perso **));
allocates space for one pointer and:
*textttt_perso = (t_perso *)malloc(sizeof(t_perso *) * numberofstructs(str));
allocates enough space for 3 pointers. At some point you need to allocate space for the actual structures.
You also have other issues. In numberofstructs() you have if(str[0]) that will cause length to always be zero. Also in numberofstructs(), you count the semi-colons. If there is data after the last sem-colon you would need to add 1 to length.
You have many other issues in this code that will show up if the data isn't perfect but here is an implementation of ft_decrypt that should work. Initial malloc should be to hold the array of pointers. Then the loop should allocate a structure for each array entry.
t_perso** ft_decrypt(char* str)
{
int i = 0;
int j = 0;
t_perso** textttt_perso;
textttt_perso = malloc(sizeof(*textttt_perso) * numberofstructs(str));
while (j <= strlen(str) && str[j])
{
if (str[j] == ';')
{
i++;
j++;
}
textttt_perso[i] = malloc(sizeof(*textttt_perso[i]));
textttt_perso[i]->age = atoi(get_data(str, j));
j = j + get_data_length(str, j) + 1;
textttt_perso[i]->name = get_data(str, j);
j = j + get_data_length(str, j);
}
return (textttt_perso);
}

How to get the most frequent letter in a word?

I am trying to get the most frequent letter in a string with only uppercased letters and no spaces between words. to do that I used a function maxArray(array, sizeof array) that gives the biggest number in the array in order to count how many times the letter is repeated and store the info in another array in the same position of each letter in the string. But in the two algorithms I have came up with it doesn't work.
PS: I am just a beginner.
Here is the code:
int maxArray(int *tab, int n) {
int i, tmp;
tmp = tab[0];
for (i = 1; i < n; i++) {
if (tmp < tab[i])
tmp = tab[i];
}
return tmp;
}
//first algo(didn't finish it)
char occurencedelettre(char *string) {
int *array;
int i, j, compt, max;
for (i = 0; string[i] !='\0'; i++) {
compt = 0;
for (j = 0; string[j] !='\0'; j++) {
if (string[i] == string[j])
compt++;
}
array[i] = compt;
}
return array;
}
//second one
char occurencedelettre(char *string) {
int count[25] = { 0 };
int x = 0;
char result;
for (int i = 0; string[i] != '\0'; i++) {
count[string[i]]++;
if (x < count[string[i]]) {
x = count[string[i]];
result = string[i];
}
}
return result;
}
The second approach is almost correct except for these problems:
the array should have a length of 26
you must subtract 'A' from the value of the letter to get an index between 0 and 25, assuming the word only contains uppercase letters in ASCII.
you must intialize result to return 0 (or any other specific value) for an empty word.
Here is a modified version:
char occurencedelettre(const char *string) {
size_t count[26] = { 0 };
size_t x = 0;
char result = '\0';
for (size_t i = 0; string[i] != '\0'; i++) {
count[string[i] - 'A']++;
if (x < count[string[i]]) {
x = count[string[i]];
result = string[i];
}
}
return result;
}
The first approach is more cumbersome and slower, but also more generic as it may work for any word contents. Here is a modified version:
char occurencedelettre(const char *string) {
char result = 0;
size_t max_count = 0;
for (size_t i = 0; string[i] != '\0'; i++) {
size_t count = 1;
for (size_t j = i + 1; string[j] != '\0'; j++) {
if (string[i] == string[j])
count++;
}
if (max_count < count) {
max_count = count;
result = string[i];
}
}
return result;
}
Note that you can adapt the first approach for any word contents, assuming 8-bit bytes:
char occurencedelettre(const char *string) {
size_t count[256] = { 0 };
size_t x = 0;
char result = '\0';
for (size_t i = 0; string[i] != '\0'; i++) {
count[(unsigned char)string[i]]++;
if (x < count[(unsigned char)string[i]]) {
x = count[(unsigned char)string[i]];
result = string[i];
}
}
return result;
}

passing char array to function from scanf in c

I have following function in c code
void analyze_text(char text[]) {
...
for (int i = 0; i < text_length || text[i] != '\0'; i++) {
...
}
}
In main function i would like to pass some string to it. If i do something like this
char text[4000] = "some text here";
analyze_text(text);
this is cool and do the goal, but i would like to have some user input present and I am not sure how to get char[] out of it. I tried following 2 and none of them seemed to work:
char text[4000];
scanf("%s",text);
analyze_text(text);
OR
char text[4000];
int c;
int count=0;
c = getchar();
count = 0;
while ((count < 4000) && (c != EOF)) {
text[count] = c;
++count;
c = getchar();
}
analyze_text(text);
I know that the first one should return pointer to char array, but second one should return char array itself, or not?
Its been like 10 years since i havent been working with c/c++. Can anybody give me some hint please?
update (whole function):
void analyze_text(char text[]) {
int printable_text_length = 0;
int text_length = strlen(text);
int word_count = 0;
int sentence_count = 0;
int in_sentence = 0;
int in_word = 0;
int count[ASCII_SIZE] = { 0 };
for (int i = 0; i < text_length || text[i] != '\0'; i++) {
int c = text[i];
if (!isspace(c)) {
printable_text_length++;
}
if (isalpha(c)) {
in_word = 1;
in_sentence = 1;
count[tolower(c)]++;
}
if (text[i] == ' ' && text[i + 1] != ' ' && in_word==1) {
word_count++;
in_word = 0;
}
if (text[i] == '.' && in_sentence==1) {
sentence_count++;
in_sentence = 0;
}
}
if (in_word == 1) { word_count++; }
if (in_sentence == 1) { sentence_count++; }
char charIndexes[ASCII_SIZE];
for (int i = 97; i <= 122; i++) {
charIndexes[i] = i;
}
for (int i=97; i <= 122; i++) {
for (int j = i + 1; j <= 122; j++) {
if (count[i] > count[j]) {
int temp = count[j];
count[j] = count[i];
count[i] = temp;
int temp2 = charIndexes[j];
charIndexes[j] = charIndexes[i];
charIndexes[i] = temp2;
}
}
}
...printf...
}
The issue with
char text[4000];
scanf("%s",text);
analyze_text(text);
is that scanf identifies space-separated chunks, so you'll only read the first one.
In order to read up to a whole line from the user, try fgets:
char text[4000];
fgets(text, 4000, stdin);
analyze_text(text);
You may want to check the return value of fgets for error detection.
You can use dyanamic array of char to pass it into the function.
Here is the code
#include <stdio.h>
#include <stdlib.h>
void analyze_text(char* text) {
for (int i = 0; text[i] != '\0'; i++) {
printf("%c\n",text[i] );
}
}
int main() {
char* text = (char *)malloc(4000 * sizeof(char));
scanf("%s", text);
analyze_text(text);
return 0;
}
and here is the output with input = 'abhishek'
a
b
h
i
s
h
e
k
remember that strlen in dyanamc array will not give the length of input array.

Numbers in char array printing as random characters

So I'm writing a somewhat simple C program that is supposed to take a string of characters separated by semicolons as input. The program is then supposed to sort the strings by length and print them to the console.
Ex: abc;12;def;1234
The issue I'm having is that any numbers that are entered end up being printed as random symbols and I'm not sure why. I'm taking in input in this function:
void get_strings(char** c)
{
while (scanf("%[^;]s", c[numStrings]) != EOF)
{
getchar();
numStrings += 1;
}
}
Since scanf is looking for strings, if numbers are entered, are they stored as the 'character form' of those numbers, or should I be casting somehow?
Here's the rest of the code:
int numStrings = 0;
void sort_strings(char** c)
{
for (int i = 0; i < numStrings; i++)
{
for (int j = 0; j < numStrings - i; j++)
{
if (strlen(c[j]) > strlen(c[j + 1]))
{
char temp[1000];
strcpy(c[j], temp);
strcpy(c[j + 1], c[j]);
strcpy(temp, c[j + 1]);
}
}
}
}
void show_strings(char** c)
{
for (int i = 0; i < numStrings; i++)
{
if (printf("%s\n", c[i]) != EOF) break;
}
}
int main()
{
char wordLen[100][1000];
char* word2[100];
for (int i = 0; i < 100; i++)
{
word2[i] = wordLen[i];
}
char** words = word2;
get_strings(words);
sort_strings(words);
show_strings(words);
return 0;
}
The parsing code is incorrect:
void get_strings(char **c) {
while (scanf("%[^;]s", c[numStrings]) != EOF) {
getchar();
numStrings += 1;
}
}
the scanf() format contains an extra s that does not match the input.
the return value of scanf() should be compared to 1 to ensure successful conversion. Conversion failure produces EOF only at end of file, otherwise it produces 0 and the contents of c[numStrings] will be indeterminate.
conversion stops at the first character ;, this character stays in the input stream, but it is read by getchar(), yet if there is an empty field, the corresponding conversion would fail and the contents of the array would be indeterminate.
you should not use a global variable for the number of strings read. You should instead return this number.
The sorting code is incorrect too:
the inner loop runs one index too far: j + 1 must be less than numStrings for all runs.
the arguments to strcpy are passed in the wrong order.
strcpy should not be used at all, you should just swap the pointers.
show_strings() always stops after the first line as printf will return the number of characters printed.
You can fix the reading loop this way:
#include <stdio.h>
#include <string.h>
int get_strings(char **c, int maxStrings) {
int numStrings = 0;
while (numStrings < maxStrings) {
switch (scanf("%999[^;]", c[numStrings])) {
case 1:
getchar();
numStrings += 1;
break;
case 0:
if (getchar() == ';') {
c[numStrings] = '\0';
numStrings += 1;
}
break;
case EOF:
return numStrings;
}
}
}
void sort_strings(char **c, int count) {
for (int i = 0; i < count; i++) {
for (int j = 0; j < count - i - 1; j++) {
if (strlen(c[j]) > strlen(c[j + 1])) {
char *temp = c[j];
c[j] = c[j + 1];
c[j + 1] = temp;
}
}
}
}
void show_strings(char **c, int count) {
for (int i = 0; i < count; i++) {
printf("%s\n", c[i]);
}
}
int main(void) {
char words[1000][100];
char *wordPtrs[100];
int numStrings;
for (int i = 0; i < 100; i++) {
wordPtrs[i] = words[i];
}
numStrings = get_strings(wordPtrs, 100);
sort_strings(wordPtrs, numStrings);
show_strings(wordPtrs, numStrings);
return 0;
}

C program to remove repeated char from a string

I came across a interview question that asked to remove the repeated char from a given string, in-place.
So if the input was "hi there" the output expected was "hi ter". It was also told to consider only alphabetic repititions and all the
alphabets were lower case. I came up with the following program. I have comments to make my logic clear. But the program does not work as expectd for some inputs. If the input is "hii" it works, but if its "hi there" it fails. Please help.
#include <stdio.h>
int main()
{
char str[] = "programming is really cool"; // original string.
char hash[26] = {0}; // hash table.
int i,j; // loop counter.
// iterate through the input string char by char.
for(i=0,j=0;str[i];)
{
// if the char is not hashed.
if(!hash[str[i] - 'a'])
{
// hash it.
hash[str[i] - 'a'] = 1;
// copy the char at index i to index j.
str[j++] = str[i++];
}
else
{
// move to next char of the original string.
// do not increment j, so that later we can over-write the repeated char.
i++;
}
}
// add a null char.
str[j] = 0;
// print it.
printf("%s\n",str); // "progamin s ely c" expected.
return 0;
}
when str[i] is a non-alphabet, say a space and when you do:
hash[str[i] - 'a']
your program can blow.
ASCII value of space is 32 and that of a is 97 so you are effectively accessing array hash with a negative index.
To solve this you can ignore non-alphabets by doing :
if(! isalpha(str[i]) {
str[j++] = str[i++]; // copy the char.
continue; // ignore rest of the loop.
}
This is going to break on any space characters (or anything else outside the range 'a'..'z') because you are accessing beyond the bounds of your hash array.
void striprepeatedchars(char *str)
{
int seen[UCHAR_MAX + 1];
char *c, *n;
memset(seen, 0, sizeof(seen));
c = n = str;
while (*n != '\0') {
if (!isalpha(*n) || !seen[(unsigned char) *n]) {
*c = *n;
seen[(unsigned char) *n]++;
c++;
}
n++;
}
*c = '\0';
}
This is code golf, right?
d(s){char*i=s,*o=s;for(;*i;++i)!memchr(s,*i,o-s)?*o++=*i:0;*o=0;}
...
// iterate through the input string char by char.
for(i=0,j=0;str[i];)
{
if (str[i] == ' ')
{
str[j++] = str[i++];
continue;
}
// if the char is not hashed.
if(!hash[str[i] - 'a'])
{
...
#include <stdio.h>
#include <string.h>
int hash[26] = {0};
static int in_valid_range (char c);
static int get_hash_code (char c);
static char *
remove_repeated_char (char *s)
{
size_t len = strlen (s);
size_t i, j = 0;
for (i = 0; i < len; ++i)
{
if (in_valid_range (s[i]))
{
int h = get_hash_code (s[i]);
if (!hash[h])
{
s[j++] = s[i];
hash[h] = 1;
}
}
else
{
s[j++] = s[i];
}
}
s[j] = 0;
return s;
}
int
main (int argc, char **argv)
{
printf ("%s\n", remove_repeated_char (argv[1]));
return 0;
}
static int
in_valid_range (char c)
{
return (c >= 'a' && c <= 'z');
}
static int
get_hash_code (char c)
{
return (int) (c - 'a');
}
char *s;
int i = 0;
for (i = 0; s[i]; i++)
{
int j;
int gap = 0;
for (j = i + 1; s[j]; j++)
{
if (gap > 0)
s[j] = s[j + gap];
if (!s[j])
break;
while (s[i] == s[j])
{
s[j] = s[j + gap + 1];
gap++;
}
}
}

Resources