find- replace string function in C - c

There are a lot of find/replace functions available on the internet, but i can't find why this is not working...( my own solution )
Here is what i tried
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
char* strrpl(char *str, char* find, char *replace)
{
int i;
char *pt = strstr(str, find), *firstStr;
firstStr = (char* )malloc(100 * sizeof(char));
// copy just until i find what i need to replace
// i tried to specify the length of firstStr just with pt - str
strncpy(firstStr, str, strlen(str) - strlen(pt));
strcat(firstStr, replace);
strcat(firstStr, pt + strlen(find));
for(i = 0; i < strlen(firstStr); i++)
str[i] = firstStr[i];
return str;
}
int main()
{
char *s, *s1, *s2;
s = (char* )malloc(100 * sizeof(char));
s1 = (char* )malloc(100 * sizeof(char));
s2 = (char* )malloc(100 * sizeof(char));
scanf("%s", s1);
scanf("%s", s2);
scanf("%s", s);
printf("%s", strrpl(s, s1, s2));
return 0;
}
The compilation gives me the error "segmentation fault" but i can't figure what memmory is he trying to alloc and he can't. I overrided a memory block or something? Please help :)
Thanks

I overrided a memory block or something?
You have:
A potential buffer overflow when you allocate firstStr. Who says the result will be less than 100 characters?
Another potential buffer overflow when you copy the answer back to the input string. Who says it will fit?
A potential buffer overflow each time you use scanf.
A memory leak each time you call malloc.
An inefficient implementation of strcpy just before return str;.
A crash (formally, undefined behaviour) when the input string does not contain the replacement string. strstr returns NULL when there is no match and you never check for it.
A potential issue with strncpy which leaves your string not NUL-terminated if there's not enough space for NUL.

Here is the immediate problem: when strstr returns NULL, your code does not pay attention. Add this line:
char *pt = strstr(str, find), *firstStr;
if (!pt) return str;
Another problem is that the call of strncpy is incorrect:
strncpy(firstStr, str, strlen(str) - strlen(pt));
it will leave firstStr unterminated, because str is longer than the substring being copied. The subsequent call
strcat(firstStr, replace);
will operate on a string that is not null-terminated, causing undefined behavior.
"Shotgun" approach to fixing it would be to use calloc instead of malloc to put zeros into firstStr. A more precise approach would be placing '\0' at the end of the copied substring.
With these fixes in place, your code runs OK (demo). However, there are several issues that need to be addressed:
You do not free any of the resources that you allocate dynamically - this results in memory leaks.
You do not compute how much memory to allocate - If a 5-character string is replaced for a 100-character string in a 100-character string, you overrun the temporary buffer.
You are using strncpy incorrectly - the function is intended for fixed-length strings. Use memcpy instead.
You are using strcat instead of memcpy or strcpy - this is inefficient.

You have not checked for the return value of strstr.
Try the below code.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
char* strrpl(char *str, char* find, char *replace)
{
int i;
char *pt = strstr(str, find);
char *firstStr;
if(pt == NULL){
printf("cannot find string \n");
return NULL;
}
firstStr = (char* )malloc(100 * sizeof(char));
// copy just until i find what i need to replace
// i tried to specify the length of firstStr just with pt - str
strncpy(firstStr, str, strlen(str) - strlen(pt));
strcat(firstStr, replace);
strcat(firstStr, pt + strlen(find));
for(i = 0; i < strlen(firstStr); i++)
str[i] = firstStr[i];
return str;
}
int main()
{
char *s, *s1, *s2, *s3;
s = (char* )malloc(100 * sizeof(char));
s1 = (char* )malloc(100 * sizeof(char));
s2 = (char* )malloc(100 * sizeof(char));
s3 = (char* )malloc(100 * sizeof(char));
scanf("%s", s);//input string
scanf("%s", s1);//string to find
scanf("%s", s2);//string to replace
s3 = strrpl(s, s1, s2);
if(s3 != NULL)
printf("%s \n",s3);
return 0;
}

Related

How to properly implement strcpy in c?

According to this:
strcpy vs strdup,
strcpy could be implemented with a loop, they used this while(*ptr2++ = *ptr1++). I have tried to do similar:
#include <stdio.h>
#include <stdlib.h>
int main(){
char *des = malloc(10);
for(char *src="abcdef\0";(*des++ = *src++););
printf("%s\n",des);
}
But that prints nothing, and no error. What went wrong?
Thanks a lot for answers, I have played a bit, and decided how best to design the loop to see how the copying is proceeding byte by byte. This seems the best:
#include <stdio.h>
#include <stdlib.h>
int main(){
char *des = malloc(7);
for(char *src="abcdef", *p=des; (*p++=*src++); printf("%s\n",des));
}
In this loop
for(char *src="abcdef\0";(*des++ = *src++););
the destination pointer des is being changed. So after the loop it does not point to the beginning of the copied string.
Pay attention to that the explicit terminating zero character '\0' is redundant in the string literal.
The loop can look the following way
for ( char *src = "abcdef", *p = des; (*p++ = *src++););
And then after the loop
puts( des );
and
free( des );
You could write a separate function similar to strcpy the following way
char * my_strcpy( char *des, const char *src )
{
for ( char *p = des; ( *p++ = *src++ ); );
return des;
}
And call it like
puts( my_strcpy( des, "abcdef" ) )'
free( des );
You are incrementing des so naturally at the end of the cycle it will be pointing past the end of the string, printing it amounts to undefined behavior, you have to bring it back to the beginning of des.
#include <stdio.h>
#include <stdlib.h>
int main(){
int count = 0;
char *des = malloc(10);
if(des == NULL){
return EXIT_FAILURE; //or otherwise handle the error
}
// '\0' is already added by the compiler so you don't need to do it yourself
for(char *src="abcdef";(*des++ = *src++);){
count++; //count the number of increments
}
des -= count + 1; //bring it back to the beginning
printf("%s\n",des);
free(dest); //to free the allocated memory when you're done with it
return EXIT_SUCCESS;
}
Or make a pointer to the beginning of des and print that instead.
#include <stdio.h>
#include <stdlib.h>
int main(){
char *des = malloc(10);
if(des == NULL){
return EXIT_FAILURE; //or otherwise handle the error
}
char *ptr = des;
for(char *src="abcdef";(*des++ = *src++);){} //using {} instead of ;, it's clearer
printf("%s\n",ptr);
free(ptr) // or free(dest); to free the allocated memory when you're done with it
return EXIT_SUCCESS;
}
printf("%s\n",des); is undefined behavior (UB) as it attempts to print starting beyond the end of the string written to allocated memory.
Copy the string
Save the original pointer, check it and free when done.
const char *src = "abcdef\0"; // string literal here has 2 ending `\0`,
char *dest = malloc(strlen(src) + 1); // 7
char *d = dest;
while (*d++ = *src++);
printf("%s\n", dest);
free(dest);
Copy the string literal
const char src[] = "abcdef\0"; // string literal here has 2 ending `\0`,
char *dest = malloc(sizeof src); // 8
for (size_t i = 0; i<sizeof src; i++) {
dest[i] = src[i];
}
printf("%s\n", dest);
free(dest);
You just need to remember the original allocated pointer.
Do not program in main. Use functions.
#include <stdio.h>
#include <stdlib.h>
size_t strSpaceNeedeed(const char *str)
{
const char *wrk = str;
while(*wrk++);
return wrk - str;
}
char *mystrdup(const char *str)
{
char *wrk;
char *dest = malloc(strSpaceNeedeed(str));
if(dest)
{
for(wrk = dest; *wrk++ = *str++;);
}
return dest;
}
int main(){
printf("%s\n", mystrdup("asdfgfd"));
}
or even better
size_t strSpaceNeedeed(const char *str)
{
const char *wrk = str;
while(*wrk++);
return wrk - str;
}
char *mystrcpy(char *dest, const char *src)
{
char *wrk = dest;
while((*wrk++ = *src++)) ;
return dest;
}
char *mystrdup(const char *str)
{
char *wrk;
char *dest = malloc(strSpaceNeedeed(str));
if(dest)
{
mystrcpy(dest, str);
}
return dest;
}
int main(){
printf("%s\n", mystrdup("asdfgfd"));
}
You allocate the destination buffer des and correctly copy the source string into place. But since you are incrementing des for each character you copy, you have moved des from the start of the string to the end. When you go to print the result, you are printing the last byte which is the nil termination, which is empty.
Instead, you need to keep a pointer to the start of the string, as well as having a pointer to each character you copy.
The smallest change from your original source is:
#include <stdio.h>
#include <stdlib.h>
int main(){
char *des = malloc(10);
char *p = des;
for(char *src="abcdef";(*p++ = *src++););
printf("%s\n",des);
}
So p is the pointer to the next destination character, and moves along the string. But the final string that you print is des, from the start of the allocation.
Of course, you should also allocate strlen(src)+1 worth of bytes for des. And it is not necessary to null-terminate a string literal, since that will be done for you by the compiler.
But that prints nothing, and no error. What went wrong?
des does not point to the start of the string anymore after doing (*des++ = *src++). In fact, des is pointing to one element past the NUL character, which terminates the string, thereafter.
Thus, if you want to print the string by using printf("%s\n",des) it invokes undefined behavior.
You need to store the address value of the "start" pointer (pointing at the first char object of the allocated memory chunk) into a temporary "holder" pointer. There are various ways possible.
#include <stdio.h>
#include <stdlib.h>
int main (void) {
char *des = malloc(sizeof(char) * 10);
if (!des)
{
fputs("Error at allocation!", stderr);
return 1;
}
char *tmp = des;
for (const char *src = "abcdef"; (*des++ = *src++) ; );
des = temp;
printf("%s\n",des);
free(des);
}
Alternatives:
#include <stdio.h>
#include <stdlib.h>
int main (void) {
char *des = malloc(sizeof(char) * 10);
if (!des)
{
fputs("Error at allocation!", stderr);
return 1;
}
char *tmp = des;
for (const char *src = "abcdef"; (*des++ = *src++) ; );
printf("%s\n", tmp);
free(tmp);
}
or
#include <stdio.h>
#include <stdlib.h>
int main (void) {
char *des = malloc(sizeof(char) * 10);
if (!des)
{
fputs("Error at allocation!", stderr);
return 1;
}
char *tmp = des;
for (const char *src = "abcdef"; (*tmp++ = *src++) ; );
printf("%s\n", des);
free(des);
}
Side notes:
"abcdef\0" - The explicit \0 is not needed. It is appended automatically during translation. Use "abcdef".
Always check the return of memory-management function if the allocation succeeded by checking the returned for a null pointer.
Qualify pointers to string literal by const to avoid unintentional write attempts.
Use sizeof(char) * 10 instead of plain 10 in the call the malloc. This ensures the write size if the type changes.
int main (void) instead of int main (void). The first one is standard-compliant, the second not.
Always free() dynamically allocated memory, since you no longer need the allocated memory. In the example above it would be redundant, but if your program becomes larger and the example is part-focused you should free() the unneeded memory immediately.

C -swap 2 chars recursively with only one pointer

I need to write a function:
void swap (char * s1, char * s2);
The function will replace the contents of both strings 1s and 2s.
Constraints:
In the function, there is no use of [] anywhere, but performance by working with pointers, in addition, trips must be made with the voters, meaning that they will actually move to another cell as needed, and will not remain in the same location all the time.
• No loops in the function, that is, work in recursion.
I did the function with pointer to pointer str** but must change it to only one pointer str and recursively. How can I change it?
#include <stdio.h>
#include <stdlib.h>
int main()
{
char *str1="abc",*str2="xyz",*pRev;
swap(&str1, &str2);
printf("str1 is %s, str2 is %s", str1, str2);
getchar();
return 0;
}
//need *str NOT **str
void swap(char **str1, char **str2);
char * RevWords (char * str, int size);
void swap(char **str1, char **str2)
{
char *temp = *str1_ptr;
*str1_ptr = *str2_ptr;
*str2_ptr = temp;
}
after swap method:
str2="abc", str1="xyz"
Well this is clearly not an ideal solution but gives you something to work with.
However this will only work (as mentioned above) when you have the same length of strings (or yes, you'll have to allocate memory + you'd need to know the length of your strings). But otherwise I think this may answer your question.
This is working with recursion and depends on the fact that both strings are the same length and containing a zero character at the end of each one.
#include <stdio.h>
#include <stdlib.h>
void swap(char* str1, char* str2)
{
// if both of them are zero characters then stop
if (*str1 == '\0' && *str2 == '\0')
return;
// else swap the contents of the pointers
else
{
char tmp = *str1;
*str1 = *str2;
*str2 = tmp;
// advance both pointer and swap them too if thye are not '\0'
swap(++str1, ++str2);
}
}
int main()
{
char str1[] = "abc\0\0\0"; // padded with zeros to be the same length as str2
char str2[] = "xyz123"; // the last '\0' is automatically added
swap(str1, str2);
printf("str1 is %s, str2 is %s", str1, str2);
getchar();
return 0;
}

Why does my function that replaces a certain character only do this to the first instance of the character?

I have this function:
char *replace_str(char *str, char *orig, char *rep)
{
static char buffer[4096];
char *p;
if(!(p = strstr(str, orig)))
return str;
strncpy(buffer, str, p-str);
buffer[p-str] = '\0';
sprintf(buffer+(p-str), "%s%s", rep, p+strlen(orig));
return buffer;
}
When I use it, it only changes the first instance of the character to be replaced. Is there any way i can make it get it to do this to all said characters?
If I understand correctly, you need a loop that reallocates the beginning of the string after the ending of your replacement string. Indeed, your function performs the action only once, since strstr returns a pointer to the first occurrence of the word orig in str.
EDIT: This may give you an idea of how to do it. I can't test it on this computer, so it might not work directly.
I did it using malloc, since static char shouldn't be used this way, in my opinion. It is supposed to store the values in buffer for a future use of the function. If you re-use this function, you would lose the previous return value of the function...
This is my way of doing it, there is certainly a more efficient way of doing it. The advantage of this function is that it adapts to any string length.
char *replace_str(char *str, char *orig, char *rep)
{
char *buffer;
char *previous_version;
char *p;
size_t occurring_position;
if (strcmp(orig, rep) == 0)
{
return(str);
}
buffer = strdup(str);
while ((p = strstr(buffer, orig)))
{
occuring_position = p - buffer;
previous_version = strdup(buffer);
free(buffer);
buffer = malloc(strlen(previous_version) + strlen(rep) - strlen(orig));
strcnpy(buffer, previous_version, occurring_position);
strcpy(buffer, rep);
strcpy(buffer + occurring_position + strlen(rep), previous_version + occurring_position + strlen(orig));
free(previous_version);
}
return (buffer);
}

Garbage with strcpy and strcat

I'm doing a client-server project in linux and I need to concatenate some strings.
I tried my code on visual studio in windows and it works fine, but it linux it gives me some garbage. I've got this function:
char* concat(char s1[], char s2[])
{
int tam = 0;
tam = strlen(s1);
tam += strlen(s2);
char *resultado = malloc(sizeof(char) * tam) ;
strcpy(resultado, s1);
strcat(resultado, s2);
return resultado;
}
I read that the problem is the missing of '\0' and I've done that:
char* concat(char s1[], char s2[])
{
int tam = 0;
tam = strlen(s1);
tam += strlen(s2);
char *resultado = malloc(sizeof(char) * tam) ;
resultado[tam+1] = '\0';
strcpy(resultado, s1);
strcat(resultado, s2);
return resultado;
}
The first 4 times that I called the function it works (the garbage disappeared), but then it gives me `malloc(): memory corruption
Anyone can help me?
You are not allocating space for the nul terminator, a very common mistake.
Suggestions:
Don't use sizeof(char) it's 1 by definition.
Check that malloc() did not return NULL.
Always remember the nul byte.
So your code would be fixed like this
char *resultado = malloc(1 + tam);
if (resultado == NULL)
pleaseDoNotUse_resultado();
Also, notice that this line
resultado[tam + 1] = '\0';
has multiple issues
tam + 1 us outside the allocated block.
You don't need to do that, strcpy() will do it for you.
Using strcat() and strcpy() in this situation is inefficient, because you already know how many bytes to copy, this
char *concat(char *s1, char *s2)
{
size_t l1;
size_t l2;
char *resultado
if ((s1 == NULL) || (s2 == NULL))
return NULL;
l1 = strlen(s1);
l2 = strlen(s2);
resultado = malloc(1 + l1 + l2) ;
if (resultado == NULL)
return NULL;
memcpy(resultado , s1, l1);
memcpy(resultado + l1, s2, l2);
resultado[l1 + l2] = '\0';
return resultado;
}
would be more efficient, even when you are checking for NULL like a paranoic freak, it would be faster than strcpy() and strcat(), because you will only compute the lengths once.
You're not allocating memory to hold the termianting null. Remember, strlen() does not count the null-terminator while calculating the string length. Nevertheless, you need that space in the destination buffer to put the null terminator.
You should write
char *resultado = malloc(tam + 1) ;
Also,
resultado[tam+1] = '\0';
is very wrong, because, array indexing is 0 based in C and here, you're overrunning the allocated memory (yes, even while allocating a size of tam+1 also) which will invoke undefined behaviour. You don't need that at all, you can get rid of that.
After that, as a side-note, as mentioned by Iharob also,
Check for the success of malloc() before using the returned pointer.
In C standard, sizeof(char) is guranteed to be 1. No need of using it while calculating the size for malloc().
You should allocate one byte more than the resulting string's length:
char *resultado = malloc(tam + 1) ;
Functions strcpy and strcat take care about the terminating NUL, you don't have to add it manually.

Concatenate string with strcpy

Here is my code
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
int main() {
char f[] = "First";
char s[] = "Second";
char *tmp = malloc(strlen(f) + strlen(s) + 2);
strcpy(tmp, f);
strcpy(tmp, s);
printf("%s", tmp);
free(tmp);
return 0;
}
I'm trying to concatenate f and s. The problem is that tmp contains only "Second" as a array.
What I miss here
strcpy copies the string to the beginning of the destination, you want strcat instead.
The second strcpy overwrites the previous one. Both copy its content to the tmp pointer (at the start of it). You should use tmp+strlen(f).
Or even better use strcat.
And even better use more secure methods like: strncpy, strncat, etc..
If you insist on using strcpy, your code should be slightly modified:
int main() {
const char *f = "First";
const char *s = "Second";
char *tmp = malloc(strlen(f) + strlen(s) + 1);
strcpy(tmp, f);
strcpy(tmp+strlen(f), s);
printf("%s", tmp);
free(tmp);
return 0;
}
You should consider using strncpy instead of strcpy for safety reasons. Also, strcat is a more conventional function for concatenating C string.
EDIT Here is an example of using strncpy instead of strcpy
#define MAX 1024
int main() {
const char *f = "First";
const char *s = "Second";
size_t len_f = min(strlen(f), MAX);
size_t len_s = min(strlen(s), MAX);
size_t len_total = len_f + len_s;
char *tmp = malloc(len_total + 1);
strncpy(tmp, f, len_f);
strncpy(tmp+len_f, s, len_s);
tmp[len_total] = '\0';
printf("%s", tmp);
free(tmp);
return 0;
}
You may want to use strcat instead of your second strcpy call, like this:
strcpy(tmp, f);
strcat(tmp, s);
Note also that allocating strlen(f) + strlen(s) + 1 bytes for tmp is sufficient no need to allocate strlen(f) + strlen(s) + 2 bytes. After concatenation, you'll get only one string, so only one null character is required.
using strcat() instead, which means append a string accroding to the MSDN doc.strcpy() just means copy a string. If you don't want to use strcat(), you should point out the position by using strncpy() or strcpy_s(). Please refer to the document.
The problem is that you copy the second string in place of the first one (the first parameter of strcpy() is where to copy the string) and this effectively overwrites the first string. Here's an idea of what you need:
size_t firstLen = strlen( f );
size_t secondLen = strlen( s );
char *tmp = malloc(firstLen + secondLen + 1);
strcpy(tmp, f);
strcpy(tmp + firstLen, s);
This can be achieved by using strcat(), although that would lead to an extra scan along the copied string.
Here is the correct idiomatic safe way to do what you want:
size_t l = strlen(f);
char *tmp = malloc(l + strlen(s) + 1);
strcpy(tmp, f);
strcpy(tmp+l, s);
or:
size_t l = strlen(f) + strlen(s) + 1;
char *tmp = malloc(l);
snprintf(tmp, l, "%s%s", f, s);
I tend to prefer the latter unless you're writing embedded systems code where you want to avoid pulling in printf dependency.
Finally, note that you should be testing malloc for failure, and that it's useless and harmful to allocate memory and copy the strings if all you want to do is print them - you could just do the following:
printf("%s%s", f, s);

Resources