I need to replace a strings in some text. I found this function here at stackoverflow:
char *replace(const char *s, const char *old, const char *new)
{
char *ret;
int i, count = 0;
size_t newlen = strlen(new);
size_t oldlen = strlen(old);
for (i = 0; s[i] != '\0'; i++) {
if (strstr(&s[i], old) == &s[i]) {
count++;
i += oldlen - 1;
}
}
ret = malloc(i + count * (newlen - oldlen));
if (ret == NULL)
exit(EXIT_FAILURE);
i = 0;
while (*s) {
if (strstr(s, old) == s) {
strcpy(&ret[i], new);
i += newlen;
s += oldlen;
} else
ret[i++] = *s++;
}
ret[i] = '\0';
return ret;
}
This function works for me fine for single replacement. But i need to replace a whole array "str2rep" to "replacement". So what i'm trying to do(im just a beginner)
****
#define MAXTEXT 39016
int l;
int j;
char *newsms = NULL;
char text[MAXTEXT];
char *str2rep[] = {":q:",":n:"};
char *replacement[] = {"?","\n"};
strcpy((char *)text,(char *)argv[5]);
l = sizeof(str2rep) / sizeof(*str2rep);
for(j = 0; j < l; j++)
{
newsms = replace(text,(char *)str2rep[j],(char *)replacement[j]);
strcpy(text,newsms);
free(newsms);
}
textlen = strlen(text);
This code even works locally, If I build it from single file... But this is asterisk module, so when this is being executed, asterisk stops with:
* glibc detected * /usr/sbin/asterisk: double free or corruption (!prev): 0x00007fa720006310 *
Issues:
ret = malloc(i + count * (newlen - oldlen)); is too small. Need + 1.
Consider what happens with replace("", "", ""). If your SO ref is this, it is wrong too.
Questionable results mixing signed/unsigned. count is signed. newlen, oldlen are unsigned.
I think the original code works OK, but I do not like using the wrap-around nature of unsigned math when it can be avoided which is what happens when newlen < oldlen.
// i + count * (newlen - oldlen)
size_t newsize = i + 1; // + 1 for above reason
if (newlen > oldlen) newsize += count * (newlen - oldlen);
if (newlen < oldlen) newsize -= count * (oldlen - newlen);
ret = malloc(newsize);
Insure enough space. #hyde Various approaches available here.
// strcpy(text, newsms);
if (strlen(newsms) >= sizeof text) Handle_Error();
strcpy(text, newsms);
Minor
No need for casts
// newsms = replace(text, (char *) str2rep[j], (char *) replacement[j]);
newsms = replace(text, str2rep[j], replacement[j]);
Better to use size_t for i. A pedantic solution would also use size_t count.
// int i;
size_t i;
I will suggest something that to me looks a bit more clear as an alternative, in place of a proper dynamic string implementation. Exception handling is left as an exercise for the reader to add. :)
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
char *appendn(char *to, char *from, int length)
{
return strncat(realloc(to, strlen(to) + length + 1), from, length);
}
char *replace(char *string, char *find, char *sub)
{
char *result = calloc(1, 1);
while (1)
{
char *found = strstr(string, find);
if (!found)
break;
result = appendn(result, string, found - string);
result = appendn(result, sub, strlen(sub));
string = found + strlen(find);
}
return appendn(result, string, strlen(string));
}
int main()
{
const char text[] = "some [1] with [2] to [3] with other [2]";
char *find[] = {"[1]", "[2]", "[3]", NULL};
char *sub[] = {"text", "words", "replace"};
char *result, *s;
int i;
result = malloc(sizeof(text));
(void) strcpy(result, text);
for (i = 0; find[i]; i ++)
{
s = replace(result, find[i], sub[i]);
free(result);
result = s;
}
(void) printf("%s\n", result);
free(result);
}
Related
I'm new to pointers and I can already see how confusing they can be.
I have tried to look this up in several threads and google but they don't quite return what I'm looking for maybe out of my inexperience.
I'm being passed an array of strings and I have to pass it again to another function however I'm extremely confused on how to do this and don't know what * or & to use or where.
My code:
#include <stdlib.h>
#include <stdio.h>
char *ft_strcat(char *dest, char *src)
{
unsigned int c;
unsigned int count;
count = 0;
while (dest[count] != 0)
{
count++;
}
c = 0;
while (src[c] != '\0')
{
dest[c + count] = src[c];
c++;
}
dest[c + count] = 0;
return (dest);
}
int size_str(char *str)
{
int c;
c = 0;
while (str[c] != '\0')
{
c++;
}
return (c - 1);
}
int size_all(int size, char *strs[], char *sep)
{
int i;
int counter;
i = 0;
counter = 0;
counter += size_str(sep) * (size - 1);
while (i < size)
{
counter += size_str(strs[i]);
i++;
}
return (counter);
}
char *ft_strjoin(int size, char **strs, char *sep)
{
int i;
char *str;
str = malloc(sizeof(char) * size_all(size, strs, sep));
str = strs[0];
i = 1;
while (i < size)
{
str = ft_strcat(str, strs[i]);
}
return (str);
}
int main(void)
{
char *sep = " ";
char a1[] = "Batata";
char a2[] = "frita";
char a3[] = "\'e";
char a4[] = "melhor";
char a5[] = "que";
char a6[] = "Banana";
char *strs[] = {a1, a2, a3, a4, a5, a6};
char *final = ft_strjoin(6, strs, sep);
printf("%s", final);
}
I thought that size all would have to have an extra dereference operator on the declaration of the function and an reference operator when I call it, but this works just fine. Am I doing something wrong or am I just misunderstanding how pointers work? Don't I have to add an extra * each time I pass it?
Finally why doesn't while (src[c] != '\0') work?
In size_str:
There's nothing wrong with while (src[c] != '\0'), but return (c - 1); is causing an off-by-one error with your string lengths. The NUL byte wasn't counted in the loop, there's no need to subtract 1.
In ft_strcat:
The first loop is repeating work that could be handled by a call to size_str.
In ft_strjoin:
str = malloc(sizeof(char) * sizeall(size, strs, sep)));
sizeof (char) is uneccessary, as it is always 1. You need an additional 1 byte added to the length passed to malloc to make room for the NUL byte in your final string.
Remember that pointers are values too. str = strs[0]; assigns the pointer held in strs[0] to the the variable str. It does not copy the contents of strs[0]. You are overwriting the value returned by malloc with a pointer to a different piece of memory.
Instead, given this set of functions, initialize the memory returned by malloc to be the empty string, by setting the first byte to NUL, and use ft_strcat to concatenate the first string.
There's no need to continually reassign the result of ft_strcat, as you are already altering str, and the return value will never change.
A complete example. One must not forget to free the resulting string when it is no longer needed.
#include <stdlib.h>
#include <stdio.h>
int size_str(char *str)
{
int i = 0;
while (str[i])
i++;
return i;
}
char *ft_strcat(char *dest, char *src)
{
int i = 0,
length = size_str(dest);
do
dest[length++] = src[i];
while (src[i++]);
return dest;
}
int size_all(int size, char **strs, char *sep)
{
int total_length = size_str(sep) * (size - 1);
for (int i = 0; i < size; i++)
total_length += size_str(strs[i]);
return total_length;
}
char *ft_strjoin(int size, char **strs, char *sep)
{
char *result = malloc(1 + size_all(size, strs, sep));
result[0] = '\0';
ft_strcat(result, strs[0]);
for (int i = 1; i < size; i++) {
ft_strcat(result, sep);
ft_strcat(result, strs[i]);
}
return result;
}
int main(void)
{
char *sep = " ";
char a1[] = "Batata";
char a2[] = "frita";
char a3[] = "\'e";
char a4[] = "melhor";
char a5[] = "que";
char a6[] = "Banana";
char *strs[] = {a1, a2, a3, a4, a5, a6};
char *final = ft_strjoin(6, strs, sep);
printf("%s\n", final);
free(final);
}
Output:
Batata frita 'e melhor que Banana
I have worked lately about this problematic, string joint. I noticed that you forgot to add an if condition where the size would be 0. Moreover, the while loop need an iteration, which means that it will give you an infinite loop.
You can find as follows some adjustment to your code:
int i;
char *str;
int j;
int k;
i = 0;
k = 0;
str = (char *)malloc(sizeof(char) * sizeall(size, strs, sep) + 1));
if (size == 0)
return (0);
while (i < size)
{
j = 0;
while (strs[i][j])
str[k++] = strs[i][j++];
j = 0;
if (i < size - 1)
while (sep[j])
str[k++] = sep[j++];
i++;
}
str[k] = '\0';
return (str);
Feel free to ask me if there is something you did not understand, and good luck.
Closed. This question needs details or clarity. It is not currently accepting answers.
Want to improve this question? Add details and clarify the problem by editing this post.
Closed 6 years ago.
Improve this question
Right now I can replace the word indifferent in the given string with nonchalant but I need to make this function dynamic so indifferent can be replaced with any word. I know i need to use malloc to create a new array that will hold my original string with the new word but don't have a strong understanding of how to use malloc yet, Please explain how to use malloc properly in this situation. Thank you.
#include <stdio.h>
#include <stdlib.h>
int findPosition(char string[], char sub[]) {
int i = 0;
int j = 0;
int f = 0;
for (i = 0; string[i] != '\0'; i++) {
if (sub[j] == string[i]) {
if (sub[j + 1] == '\0') {
f = 1;
break;
}
j++;
} else
j = 0;
}
if (f == 1) {
return i - j;
}
return -1;
}
int findLength(char sub[]) {
int i = 0;
for (i = 0; sub[i] != '\0'; i++) {
}
return i;
};
void replaceWord(char string[], char sub[], char replace[]) {
int i = 0;
int j = 0;
int p = findPosition(string, sub);
int l = findLength(sub);
int k = p + l - 1;
for (i = p; i < k; i++) {
string[i] = replace[j];
j++;
}
while(string[k] != '\0') {
string[k] = string[k + 1];
k++;
}
}
int main(int argc, const char *argv[]) {
char stringArray[120] = "\"Mr.Fay, is this going to be a battle of wits? \""
"\t\"If it is,\" was the indifferent retort, \""
"you have come unarmed!\"";
replaceWord(stringArray, "indifferent", "nonchalant");
int i = 0;
while (stringArray[i] != '\0') {
printf("%c", stringArray[i]);
i++;
}
return 0;
};
You can use malloc to allocate memory for the new string where you replace each occurrence of one word with another word:
#include <stdio.h>
#include <stdlib.h>
/* use a local implementation of the string functions: */
size_t my_strlen(const char *s) {
size_t len;
for (len = 0; s[len] != '\0'; len++)
continue;
return len;
}
void *my_memcpy(void *dest, const void *src, size_t n) {
size_t i;
for (i = 0; i < n; i++) {
((unsigned char*)dest)[i] = ((unsigned char*)src)[i];
}
return dest;
}
char *my_strdup(const char *s) {
size_t n = my_strlen(s) + 1;
char *p = malloc(n);
if (p) my_memcpy(p, s, n);
return p;
}
char *my_strstr(const char *s1, const char *s2) {
for (;; s1++) {
for (size_t i = 0;; i++) {
if (s2[i] == '\0') return s1;
if (s1[i] != s2[i]) break;
}
if (*s1 == '\0') return NULL;
}
}
char *replaceWord(const char *str, const char *s1, const char *s2) {
char *res = my_strdup(str); /* return value is always allocated */
char *p, *q;
size_t offset = 0;
size_t len = my_strlen(str);
size_t len1 = my_strlen(s1);
size_t len2 = my_strlen(s2);
if (len1 == 0)
return res;
while ((p = my_strstr(res + offset, s1)) != NULL) {
offset = p - res;
if (len1 == len2) {
/* no need to reallocate, replace in place */
my_memcpy(res + offset, s2, len2);
} else {
/* allocate a new array with the adjusted length */
q = malloc(len + len2 - len1 + 1);
/* copy the beginning of the string */
my_memcpy(q, res, offset);
/* copy the replacement string */
my_memcpy(q + offset, s2, len2);
/* copy the remainder of the string, and the final '\0' */
my_memcpy(q + offset + len2, res + offset + len1, len - offset - len1 + 1);
/* free the previous string */
free(res);
res = q;
}
/* search for matches from the end of the replacement */
offset += len2;
}
return res;
}
int main(int argc, const char *argv[]) {
char stringArray[120] = "\"Mr.Fay, is this going to be a battle of wits? \""
"\t\"If it is,\" was the indifferent retort, \""
"you have come unarmed!\"";
char *p = replaceWord(stringArray, "indifferent", "nonchalant");
printf("%s", p);
free(p);
return 0;
}
I got a segment fault error at the line with the comments that contains lots of equals signs below.
The function below str_spit, I wrote it because I want to split a string using a specific char, like a comma etc.
Please help.
int str_split(char *a_str, const char delim, char *** result)
{
int word_length = 0;
int cur_cursor = 0;
int last_cursor = -1;
int e_count = 0;
*result = (char **)malloc(6 * sizeof(char *));
char *char_element_pos = a_str;
while (*char_element_pos != '\0') {
if (*char_element_pos == delim) {
char *temp_word = malloc((word_length + 1) * sizeof(char));
int i = 0;
for (i = 0; i < word_length; i++) {
temp_word[i] = a_str[last_cursor + 1 + i];
}
temp_word[word_length] = '\0';
//
*result[e_count] = temp_word;//==============this line goes wrong :(
e_count++;
last_cursor = cur_cursor;
word_length = 0;
}
else {
word_length++;
}
cur_cursor++;
char_element_pos++;
}
char *temp_word = (char *) malloc((word_length + 1) * sizeof(char));
int i = 0;
for (i = 0; i < word_length; i++) {
temp_word[i] = a_str[last_cursor + 1 + i];
}
temp_word[word_length] = '\0';
*result[e_count] = temp_word;
return e_count + 1;
}
//this is my caller function====================
int teststr_split() {
char delim = ',';
char *testStr;
testStr = (char *) "abc,cde,fgh,klj,asdfasd,3234,adfk,ad9";
char **result;
int length = str_split(testStr, delim, &result);
if (length < 0) {
printf("allocate memroy failed ,error code is:%d", length);
exit(-1);
}
free(result);
return 0;
}
I think you mean
( *result )[e_count] = temp_word;//
instead of
*result[e_count] = temp_word;//
These two expressions are equivalent only when e_count is equal to 0.:)
[] has a higher precedence than *, so probably parentheses will solve THIS problem:
(*result)[e_count] = temp_word;
I didn't check for more problems in the code. Hint: strtok() might do your job just fine.
I'm new in programming in C and I got 2 problems. I have two string, then I need to split them into words and find out if both strings contains same words. I will explain it with my code.
Input:
"He said he would do it."
"IT said: 'He would do it.'"
This two string are placed into two arrays. At first I need to parse words from others characters.
Call function:
char ** w1 = parse(s1, &len1);
Variable len counts number of rows (words).
Function parse:
char ** parse(char *w, int * i)
{
int j = 0, y, dupl = 0; //variables for cycles and duplicate flag
char d[] = " <>[]{}()/\"\\+-*=:;~!##$%^&_`'?,.|";
char* token = strtok(w, d);
unsigned len = strlen(token), x;
unsigned plen = len;
char ** f = (char**) malloc(len * sizeof (char*));
while (token != NULL)
{
len = strlen(token);
for (x = 0; x < len; x++)
{
token[x] = tolower(token[x]);
}
for (y = 0; y < *i; y++) //cycle for deleting duplicated words
{
if (equals(token, f[y]) == 1)
{
dupl = 1; break;
}
}
if (dupl == 1)
{
token = strtok(NULL, d);
dupl = 0;
continue;
}
if (len >= plen)
{
f = (char**) realloc(f, (len+1) * sizeof (char*));
plen = len;
}
else
f = (char**) realloc(f, (plen+1) * sizeof (char*));
f[j] = token;
token = strtok(NULL, d);
*i = *i + 1;
j++;
}
free(token);
return f;
}
Ok, now i have 2x 2Darrays, then just sort it (qsort(w1, len1, sizeof (char*), cmp);) and compare it:
for (i = 0; i < len2; i++)
if (equals(w1[i], w2[i]) == 0)
return 0;
Function equals:
int equals(char *w1, char *w2)
{
if (strcmp(w1, w2) == 0)
return 1;
return 0;
}
I know that all of this can be faster, but at first I need to solve my problem. This works for input which I wrote at the beginning, but when I type something long e.g.500 characters, my result is Aborted. I think that the problem is here:
f = (char**) realloc(f, (len+1) * sizeof (char*));
but dunno why.
Second thing is, that I can't free my arrays. This
void Clear (char ** w, int max)
{
int i;
for (i = 0; i < max; i++)
free(w[i]);
free(w);
}
gives me a segmentation fault.
Thanks for your time and I'm sorry for my bad english and bad programming skills.
It seems to have confused the word length and number of words in the parse function in your logic.
char ** f = (char**) malloc(len * sizeof (char*));
I think for example of len in portions of the above are as should be the number of words rather than characters.
In Python, one can print XXXX using print "X"*4; Is there any easy equivalent in C?
C doesn't have much in the way of builtin/standard library, so you'd have to go through the process of allocating a new buffer and concatenating four strings to it, then freeing the buffer, it's simpler just to print "X" four times.
int i;
for (i=0; i<4; ++i) {
printf("X");
}
printf("\n");
You could write your own, though.
char* x(const char* s, unsigned int reps) {
size_t len = strlen(s);
char* buf = (char*)malloc(len*reps + 1);
while (reps--) {
memcpy(buf, s, len);
buf += len;
}
*buf = '\0';
return buf;
}
char* repeated = x("X", 4);
printf("%s\n", repeated);
free(repeated);
But as you can see, the result is almost as long.
Yes. Welcome to C:
int i;
for (i=0; i<4; i++)
printf("X");
Not really. In C you would need to use a loop.
No there is no standard library in C that can do that, but you can always implement easily with yourself. A simple implementation in C:
char * strmul(const char * src, size_t times) {
size_t s_len = strlen(src);
char * res = (char*)malloc(s_len * times + 1);
int i;
for(i = 0; i < times; i++) {
strcpy(res + s_len * i, src);
}
return res;
}
Improved version as suggested by #ikegami
char * strmul(const char * src, size_t times) {
size_t s_len = strlen(src);
char * res = (char*)malloc(s_len * times + 1);
int i;
for(i = 0; i < times; i++) {
memcpy(res + s_len * i, src);
}
res[s_len * times + 1] = '\0';
return res;
}