My string concatination is doubling its result each time, why? - c

I'm basically just taking a string and appending / concatting with another string. The first run through produces the desired results, but the 2nd, 3rd and so on results seem to be doubling the src string. Combining things with jQuery is super simple, not sure whats going on here in C. Should I be using memset? or calloc?
#include <stdio.h>
#include <string.h>
int main(void) {
const char* name = "Michelle";
char *ptr;
char dest[30];
char yourName[30];
char dots[] = "..";
int i;
for (i=0;i<4;i++)
{
if (strlen(name) > 5)
{
sprintf(yourName, "%s", name);
strncpy(dest, yourName, 3);
ptr = strcat(dest, dots);
sprintf(yourName, "%s", ptr);
printf("%s\n", yourName);
}
}
return 0;
}
I'm expecting to see results such as
Michelle becomes Mic..
This works, however if my name structure has 4 names and they were all Michelle the results are...
Mic..
Mic....
Mic......
Mic........

You didn't heed the following warning:
The strncpy() function is similar, except that at most n bytes of src are copied. Warning: If there is no null byte among the first n bytes of src, the string placed in dest will not be null-terminated.
Replace
ptr = strncpy(dest, yourName, 3);
strcat(dest, dots);
sprintf(yourName, "%s", ptr);
with
ptr = strncpy(dest, yourName, 3);
dest[3] = '\0';
strcat(dest, dots);
sprintf(yourName, "%s", ptr);
or just
yourName[3] = '.';
yourName[4] = '.';
yourName[5] = '\0';

Related

Segmentation fault when removing trailing whitespace from string in c

I am trying to remove the white spaces at the end of a string. I have the following code:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
char *trim(char *str) {
char *end;
end = str + strlen(str) - 1;
while(end > str && isspace((unsigned char)*end)) {
end--;
}
*(end++) = '\0'; //this line causes the segfault
return str;
}
int main(void) {
char *str = "mystring ";
printf("before trim string is %s\n", str);
printf("length before: %d\n", strlen(str)); //should be 10
str = trim(str);
printf("after trim string is %s\n", str);
printf("length after: %d\n", strlen(str)); //should be 8
return 0;
}
When I run the code, I get a segmentation fault. What I'm not sure about is why incrementing the pointer 'end' by 1, and then changing the value it points to from a white space to the null terminator causes the segfault. I have read that incrementing a pointer in C such that the pointer does not point to an element of an array is undefined behaviour. However, wouldn't the white spaces at the end of "mystring" still be part of the char array str[]? Any help is really appreciated.
The issue here is that the line causing the crash is attempting to modify a string literal, which results in undefined behavior.
According to https://en.cppreference.com/w/c/language/string_literal ,
String literals are not modifiable (and in fact may be placed in read-only memory such as .rodata). If a program attempts to modify the static array formed by a string literal, the behavior is undefined.
You'll have to make sure to pass a char* to a modifiable array, such as one you allocate yourself with malloc().
The other responses are correct - but i'm surprised your compiler did not pick up the error. Below compiles and runs fine with Visual Studio 2017.
Note a little bug also in trim()
*(++end) = '\0'; NOT
*(end++) = '\0';
All good now.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
char *trim(char *str) {
char *end;
end = str + strlen(str) - 1;
while (end > str && isspace((unsigned char)*end)) {
end--;
}
*(++end) = '\0'; //this line causes the segfault
return str;
}
int main(void) {
char str0[] = "mystring "; // minor change here
char *str = str0; // minor change here
printf("before trim string is %s\n", str);
printf("length before: %d\n", strlen(str)); //should be 10
str = trim(str);
printf("after trim string is %s\n", str);
printf("length after: %d\n", strlen(str)); //should be 8
return 0;
}
When a string is processed with a pointer, the compiler assigns the storage address of the first character of the constant string after "=" to the pointer, and the constant string exists in the read-only memory.
You can try these two code to observation difference:
int main()
{
char str[] = "abcd";
*str = 'c';
printf("%s\n", str);
return 0;
}
int main()
{
char *str = "abcd";
*str = 'c'; // error
printf("%s", str);
return 0;
}

strcpy() Delete everything else

I need to read a file and look for a word and replace with a new word but it's not working as it should:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main() {
FILE * f = fopen("text" , "rb" );
if(f == NULL ){
perror("error ");
return 1;
}
char chain[100];
while(!feof(f)){
fgets(chain, 100 , f);
}
printf("%s", chain);
fclose(f);
printf("\n \n ;D \n");
return 0;
}
And this is how I replace the old word:
char str[] ="This is a hiall samplesss friends string";
char * pch;
pch = strstr (str,"hiall");
strncpy (pch,"sam",5);
puts (str);
thanks
That strncpy is copying 5 chars from "sam" into pch. Note however that "sam" has only 3 characters, so it is copying the three characters plus the \0 terminator. That's why it is "deleting" the rest of the string: it is adding a terminator right after "sam", so you are getting This is a sam in the output.
If you strncpy only 3 characters, you'll get this:
This is a samll samplesss friends string
If what you want is something like find-and-replace (i.e. replacing "hiall" with "sam" and getting This is a sam samplesss friends string), you need to move the rest of the string backwards. A simple strncpy won't work because the destination memory overlaps with the target. For this, you can either use memmove, or use an auxiliary buffer:
char str[] = "This is a hiall samplesss friends string";
char* pch;
char* old_word = "hiall";
char* new_word = "sam";
size_t len_old = strlen(old_word);
size_t len_new = strlen(new_word);
pch = strstr(str, old_word);
assert(len_new <= len_old);
if (pch) {
char* rest = pch + len_old;
size_t len_rest = strlen(rest);
char* aux = malloc(len_rest + 1);
strncpy(aux, rest, len_rest + 1);
strncpy(pch, new_word, len_new);
strncpy(pch + len_new, aux, len_rest + 1);
free(aux);
puts(str);
}
Note that this will only work if new_word is the same size or shorter than old_word. If new_word is longer, you won't be able to edit in-place (in the string str itself), unless the original string has extra memory for it (e.g. if you declared it with str[1000] to guarantee you can increase its size enough — then the code above would work). The safest approach if you can't plan ahead what size new_word is going to be would be to allocate a new string:
char str[] ="This is a hiall samplesss friends string";
char* pch;
char* old_word = "hiall";
char* new_word = "sam";
pch = strstr(str, old_word);
size_t len_str = strlen(str);
size_t len_new = strlen(new_word);
size_t len_old = strlen(old_word);
if (pch) {
char* new_str = malloc(len_str - len_old + len_new + 1);
ptrdiff_t pos_word = pch - str;
strncpy(new_str, str, pos_word);
strncpy(new_str + pos_word, new_word, len_new);
strncpy(new_str + pos_word + len_new, pch + len_old,
len_str - pos_word - len_old + 1);
puts(new_str);
}
(Edit: addressed issues pointed out by David Bowling in the comments.)

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);

Recursive C String replace

I'm quite new to C programming, used to only use C++ with the String class, but I'm wondering how I would go about doing a recursive replacement of a string with another string.
My code is like this, but it doesn't seem to work correctly and I cannot quite pin down where it fails. It works fine on one replacement, but more than one and it fails.
#include <stdio.h>
#include <string.h>
char *replace_str(char *str, char *orig, char *rep)
{
int current_index = 0;
static char buffer[10000];
if (!strstr(str, orig)) // Is 'orig' even in 'str'?
{
return str;
}
while (1)
{
char *p;
if (!(p = strstr(str + current_index, orig))) // Is 'orig' even in 'str'?
{
return buffer;
}
strncpy(buffer, str, p-str); // Copy characters from 'str' start to 'orig' st$
buffer[p-str] = '\0';
sprintf(buffer+(p-str), "%s%s", rep, p+strlen(orig));
printf("%d -> %s\n", current_index, buffer);
current_index = (p - str) + strlen(rep);
str = buffer;
}
return buffer;
}
int main(void)
{
puts(replace_str("hello world world", "world", "world2"));
return 0;
}
With this example, it prints this:
0 -> hello world2 world
12 -> hello world2 world22
hello world2 world22
It could be not the best implementation, but here you find a stringReplace function that does the task.
About your code. First, it is better the caller supplies its dest buffer instead of having a static buffer into the function. Then, you do not check for buffer overflow.
Your
strncpy(buffer, str, p-str); // Copy characters from 'str' start to 'orig' st$
will copy from A to A except in the first iteration. This is not good, the buffer shouldn't overlap. Use memmove instead.
But the whole idea is not clean since you update the same buffer you use as source to catch other occurrences.
At some point you overwrite the input (when str and buffer points to the same thing) loosing information since your replacing word is longer than the original to be replaced so you do not preserve the "original next character". (If you try with "work" instead of "world2", it should work)...
So your current_index should index the original string str (and you'll never do str = buffer), and you will append to your internal buffer the part you need (up to an occurence of "world" if found then append "world2", update current_index by the length of "world" and go on).
I would do (trying to keep you original idea, more or less)
#include <stdio.h>
#include <string.h>
char *replace_str(char *str, const char *orig, const char *rep)
{
size_t buf_index = 0;
static char buffer[10000];
if (!strstr(str, orig)) // Is 'orig' even in 'str'?
{
return str;
}
buffer[0] = 0;
for(;;)
{
char *p;
if (!(p = strstr(str, orig)))
{
strcpy(buffer + buf_index, str);
return buffer;
}
strncpy(buffer + buf_index, str, p - str);
strcpy(buffer + buf_index + (p - str), rep);
buf_index += (p-str) + strlen(rep);
str = p + strlen(orig);
}
return buffer;
}
int main(void)
{
puts(replace_str("hello world world world", "wor", "world2"));
return 0;
}
The problem is str = buffer; . You are effectively changing the source pointer, and that screwing up your code.
Use the below code before the start of the while loop
char bk[100]
strcpy(bk,str);
and replace all str occurrences in the while loop with bk.It will work.
use this recursive function rplcStr (), it's coded as simple replace c++.
string rplcStr(string x, string y, string z){
// Done by Raafat Maurice in 29 Feb 2012
// this function will replace all string (y) found in string (x) by the string (z).
if (x.find(y.c_str(),0) != -1 ) {
return (rplcStr (x.substr(0, x.find(y.c_str(),0) ) + z + x.substr( x.find(y.c_str(),0) + y.size() ) ,y,z));
}
else {
return (x);
}
}

How to concatenate two strings where the source string should be appended before the destination string?

I'm stuck at yet another C problem. How can I concatenate two strings with the second string being inserted before the first string?
This is what I came up with. Unfortunately I'm stuck at all these pointer to chars, char arrays et cetera.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main(int argc, char* argv[] )
{
char* output;
int i;
for(i = 9; i > 0; i--)
{
unsigned int value = (unsigned int)i;
char buffer[20];
sprintf(buffer, "%u", value);
// strcat(ouput, buffer); // append before the string.
// first loop: 9
// second loop: 89
// third loop: 789
}
printf("%s", output);
}
How must I correct my code to make it work? I guess I have to somehow set the output variable to empty. When do I need fixed widths for the char array or the pointer? 20 was just a random guess from me.
I'm very confused, as your posted code has absolutely nothing to do with the problem you state. (Well, they both use strings, but that's about it)
char* src = "Hello, ";
char* dest = "World!";
char* temp;
temp = malloc(strlen(src) +strlen(dest) + 1);
strcpy(temp, src);
strcat(temp, dest);
dest = temp;
Unless dest is a fixed buffer of adequate size for the combined string. If so, then replace the last line with:
strcpy(dest, temp);
free(temp);
Now, if you want to specifically build the list of digits backwards, let's try a different tack:
char buffer[10];
buffer[9] = '\0'; // null terminate our string.
char* output;
int i;
for(i = 9; i > 0; i--)
{
// this is a fast way of saying, sprintf("%u", i);
// works only for single digits
char d = (char)('0' + i);
buffer[i-1] = d;
output = &buffer[i-1];
printf("%s", output);
}
Usually, you should just avoid the situation to start with. The most obvious solution for your example would be to simply count upward to start with. When that's not suitable, a recursive solution to reverse the order in which the string is built can still allow you to generate the string from beginning to end:
int build_string(int value, char *string) {
char temp[10];
if (value > -1)
build_string(value-1, string);
sprintf(temp, "%d", value); // use snprintf if available.
strcat(string, temp);
return string;
}
int main() {
char result[20] = {0};
build_string(9, result);
printf("%s", result);
return 0;
}
You can append the integer at the end of the string as:
int i;
char buffer[20];
for(i = 0; i < 10; i++) {
sprintf(buffer+i, "%u", i);
}
printf("%s", buffer); // prints 0123456789
For your stated problem (insert one string in front of another), this code will do the job - but has no error checking. It assumes there is enough space in the target buffer for the existing string and the new prefix:
/* Insert string t in front of string s in string s */
char *strinsert(char *s, const char *t)
{
char *p = s + strlen(s);
char *q = p + strlen(t);
char *r = s;
while (p >= s)
*q-- = *p--;
while (*t)
*s++ = *t++;
return(r);
}
What it does is copy the existing string up by the correct number of places so that there is space for the new string at the beginning.
Assuming that the destination buffer is big enough and that the source and destination do not overlap:
// not sure what order to put the params - the usual C way is destination
// followed by source, but it's also potentially confusing that the result of
// prepend(foo,bar) is "<bar><foo>".
char* prepend(char *restrict dest, const char *restrict src) {
size_t len = strlen(src);
memmove(dest + len, dest, strlen(dest));
return memcpy(dest, src, len);
}
If the buffers may overlap (for example, if src is the second half of dest), this approach doesn't work.
If the destination buffer is not big enough, then someone has to allocate new memory for the result, in which case the question of which is the "source" and which the "destination" disappears - they're both "source" and neither is "destination".

Resources