Cannot assign chars from one string to another - c

So I have a function that takes a string, and strips out special format characters, and assigns it to another string for later processing.
A sample call would be:
act_new("$t does $d");
It should strip out the $t and the $d and leave the second string as " does ", but its not assigning anything. I am getting back into programming after quite a few years of inactivity, and this is someone elses code (A MUD codebase, Rom), but I feel like I am missing something fundamental with pointer assignments. Any tips?
(This is truncated code, the rest has no operations on str or point until much later)
void act_new(const char *format)
{
const char *str;
char *point;
str = format;
while ( *str != '\0' ) {
if ( *str != '$' ) {
*point++ = *str++;
continue;
}
}
}

You need to increment str every time through the loop, not only when you assign to point. Otherwise you end up in an infinite loop when the character doesn't match the if condition.
You also want to skip the character after $, so you have to increment str twice when you encounter $.
The code is simpler if you use a for loop and array indexing rather than pointer arithmetic.
size_t len = strlen(format);
for (size_t i = 0; i < len; i++) {
if (format[i] == '$') {
i++; // extra increment to skip character after $
} else {
*point++ = format[i];
}
}

There are a few problems with your code, as pointed out in the comments:
point is not initialized (garbage pointer value)
continue doesn't do anything
infinite loop if a $ is encountered
When writing the function, one must also keep in mind to skip an extra character if a $ is encountered if and only if it's not the last character in the string (except for the '\0').
Since you know how many times you need to loop, a for loop is better suited and, as a bonus, you don't have to explicitly check if the character after a $ is '\0' when skipping an extra character in format (renamed src below). Also, don't forget to terminate the destination string.
This code will take care of those things for you:
void act_new(const char *src)
{
const size_t length = strlen(src);
char * const dst = (char*)malloc(sizeof(char)*(length+1));
if(dst == NULL)
// Error handling left out
return;
char *point = dst;
for(size_t i = 0; i < length; ++i)
{
if(src[i] == '$')
{
++i;
continue;
}
*point++ = src[i];
}
*point = '\0'; //Terminate string properly
printf("%s\n", dst);
free(dst);
}

Related

String Palindrome function that doesn't work as intended in C [duplicate]

What is the easiest and most efficient way to remove spaces from a string in C?
Easiest and most efficient don't usually go together…
Here's a possible solution for in-place removal:
void remove_spaces(char* s) {
char* d = s;
do {
while (*d == ' ') {
++d;
}
} while (*s++ = *d++);
}
Here's a very compact, but entirely correct version:
do while(isspace(*s)) s++; while(*d++ = *s++);
And here, just for my amusement, are code-golfed versions that aren't entirely correct, and get commenters upset.
If you can risk some undefined behavior, and never have empty strings, you can get rid of the body:
while(*(d+=!isspace(*s++)) = *s);
Heck, if by space you mean just space character:
while(*(d+=*s++!=' ')=*s);
Don't use that in production :)
As we can see from the answers posted, this is surprisingly not a trivial task. When faced with a task like this, it would seem that many programmers choose to throw common sense out the window, in order to produce the most obscure snippet they possibly can come up with.
Things to consider:
You will want to make a copy of the string, with spaces removed. Modifying the passed string is bad practice, it may be a string literal. Also, there are sometimes benefits of treating strings as immutable objects.
You cannot assume that the source string is not empty. It may contain nothing but a single null termination character.
The destination buffer can contain any uninitialized garbage when the function is called. Checking it for null termination doesn't make any sense.
Source code documentation should state that the destination buffer needs to be large enough to contain the trimmed string. Easiest way to do so is to make it as large as the untrimmed string.
The destination buffer needs to hold a null terminated string with no spaces when the function is done.
Consider if you wish to remove all white space characters or just spaces ' '.
C programming isn't a competition over who can squeeze in as many operators on a single line as possible. It is rather the opposite, a good C program contains readable code (always the single-most important quality) without sacrificing program efficiency (somewhat important).
For this reason, you get no bonus points for hiding the insertion of null termination of the destination string, by letting it be part of the copying code. Instead, make the null termination insertion explicit, to show that you haven't just managed to get it right by accident.
What I would do:
void remove_spaces (char* restrict str_trimmed, const char* restrict str_untrimmed)
{
while (*str_untrimmed != '\0')
{
if(!isspace(*str_untrimmed))
{
*str_trimmed = *str_untrimmed;
str_trimmed++;
}
str_untrimmed++;
}
*str_trimmed = '\0';
}
In this code, the source string "str_untrimmed" is left untouched, which is guaranteed by using proper const correctness. It does not crash if the source string contains nothing but a null termination. It always null terminates the destination string.
Memory allocation is left to the caller. The algorithm should only focus on doing its intended work. It removes all white spaces.
There are no subtle tricks in the code. It does not try to squeeze in as many operators as possible on a single line. It will make a very poor candidate for the IOCCC. Yet it will yield pretty much the same machine code as the more obscure one-liner versions.
When copying something, you can however optimize a bit by declaring both pointers as restrict, which is a contract between the programmer and the compiler, where the programmer guarantees that the destination and source are not the same address. This allows more efficient optimization, since the compiler can then copy straight from source to destination without temporary memory in between.
In C, you can replace some strings in-place, for example a string returned by strdup():
char *str = strdup(" a b c ");
char *write = str, *read = str;
do {
if (*read != ' ')
*write++ = *read;
} while (*read++);
printf("%s\n", str);
Other strings are read-only, for example those declared in-code. You'd have to copy those to a newly allocated area of memory and fill the copy by skipping the spaces:
char *oldstr = " a b c ";
char *newstr = malloc(strlen(oldstr)+1);
char *np = newstr, *op = oldstr;
do {
if (*op != ' ')
*np++ = *op;
} while (*op++);
printf("%s\n", newstr);
You can see why people invented other languages ;)
#include <ctype>
char * remove_spaces(char * source, char * target)
{
while(*source++ && *target)
{
if (!isspace(*source))
*target++ = *source;
}
return target;
}
Notes;
This doesn't handle Unicode.
if you are still interested, this function removes spaces from the beginning of the string, and I just had it working in my code:
void removeSpaces(char *str1)
{
char *str2;
str2=str1;
while (*str2==' ') str2++;
if (str2!=str1) memmove(str1,str2,strlen(str2)+1);
}
#include<stdio.h>
#include<string.h>
main()
{
int i=0,n;
int j=0;
char str[]=" Nar ayan singh ";
char *ptr,*ptr1;
printf("sizeof str:%ld\n",strlen(str));
while(str[i]==' ')
{
memcpy (str,str+1,strlen(str)+1);
}
printf("sizeof str:%ld\n",strlen(str));
n=strlen(str);
while(str[n]==' ' || str[n]=='\0')
n--;
str[n+1]='\0';
printf("str:%s ",str);
printf("sizeof str:%ld\n",strlen(str));
}
The easiest and most efficient way to remove spaces from a string is to simply remove the spaces from the string literal. For example, use your editor to 'find and replace' "hello world" with "helloworld", and presto!
Okay, I know that's not what you meant. Not all strings come from string literals, right? Supposing this string you want spaces removed from doesn't come from a string literal, we need to consider the source and destination of your string... We need to consider your entire algorithm, what actual problem you're trying to solve, in order to suggest the simplest and most optimal methods.
Perhaps your string comes from a file (e.g. stdin) and is bound to be written to another file (e.g. stdout). If that's the case, I would question why it ever needs to become a string in the first place. Just treat it as though it's a stream of characters, discarding the spaces as you come across them...
#include <stdio.h>
int main(void) {
for (;;) {
int c = getchar();
if (c == EOF) { break; }
if (c == ' ') { continue; }
putchar(c);
}
}
By eliminating the need for storage of a string, not only does the entire program become much, much shorter, but theoretically also much more efficient.
/* Function to remove all spaces from a given string.
https://www.geeksforgeeks.org/remove-spaces-from-a-given-string/
*/
void remove_spaces(char *str)
{
int count = 0;
for (int i = 0; str[i]; i++)
if (str[i] != ' ')
str[count++] = str[i];
str[count] = '\0';
}
Code taken from zString library
/* search for character 's' */
int zstring_search_chr(char *token,char s){
if (!token || s=='\0')
return 0;
for (;*token; token++)
if (*token == s)
return 1;
return 0;
}
char *zstring_remove_chr(char *str,const char *bad) {
char *src = str , *dst = str;
/* validate input */
if (!(str && bad))
return NULL;
while(*src)
if(zstring_search_chr(bad,*src))
src++;
else
*dst++ = *src++; /* assign first, then incement */
*dst='\0';
return str;
}
Code example
Exmaple Usage
char s[]="this is a trial string to test the function.";
char *d=" .";
printf("%s\n",zstring_remove_chr(s,d));
Example Output
thisisatrialstringtotestthefunction
Have a llok at the zString code, you may find it useful
https://github.com/fnoyanisi/zString
That's the easiest I could think of (TESTED) and it works!!
char message[50];
fgets(message, 50, stdin);
for( i = 0, j = 0; i < strlen(message); i++){
message[i-j] = message[i];
if(message[i] == ' ')
j++;
}
message[i] = '\0';
Here is the simplest thing i could think of. Note that this program uses second command line argument (argv[1]) as a line to delete whitespaces from.
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
/*The function itself with debug printing to help you trace through it.*/
char* trim(const char* str)
{
char* res = malloc(sizeof(str) + 1);
char* copy = malloc(sizeof(str) + 1);
copy = strncpy(copy, str, strlen(str) + 1);
int index = 0;
for (int i = 0; i < strlen(copy) + 1; i++) {
if (copy[i] != ' ')
{
res[index] = copy[i];
index++;
}
printf("End of iteration %d\n", i);
printf("Here is the initial line: %s\n", copy);
printf("Here is the resulting line: %s\n", res);
printf("\n");
}
return res;
}
int main(int argc, char* argv[])
{
//trim function test
const char* line = argv[1];
printf("Here is the line: %s\n", line);
char* res = malloc(sizeof(line) + 1);
res = trim(line);
printf("\nAnd here is the formatted line: %s\n", res);
return 0;
}
This is implemented in micro controller and it works, it should avoid all problems and it is not a smart way of doing it, but it will work :)
void REMOVE_SYMBOL(char* string, uint8_t symbol)
{
uint32_t size = LENGHT(string); // simple string length function, made my own, since original does not work with string of size 1
uint32_t i = 0;
uint32_t k = 0;
uint32_t loop_protection = size*size; // never goes into loop that is unbrakable
while(i<size)
{
if(string[i]==symbol)
{
k = i;
while(k<size)
{
string[k]=string[k+1];
k++;
}
}
if(string[i]!=symbol)
{
i++;
}
loop_protection--;
if(loop_protection==0)
{
i = size;
break;
}
}
}
While this is not as concise as the other answers, it is very straightforward to understand for someone new to C, adapted from the Calculix source code.
char* remove_spaces(char * buff, int len)
{
int i=-1,k=0;
while(1){
i++;
if((buff[i]=='\0')||(buff[i]=='\n')||(buff[i]=='\r')||(i==len)) break;
if((buff[i]==' ')||(buff[i]=='\t')) continue;
buff[k]=buff[i];
k++;
}
buff[k]='\0';
return buff;
}
I assume the C string is in a fixed memory, so if you replace spaces you have to shift all characters.
The easiest seems to be to create new string and iterate over the original one and copy only non space characters.
I came across a variation to this question where you need to reduce multiply spaces into one space "represent" the spaces.
This is my solution:
char str[] = "Put Your string Here.....";
int copyFrom = 0, copyTo = 0;
printf("Start String %s\n", str);
while (str[copyTo] != 0) {
if (str[copyFrom] == ' ') {
str[copyTo] = str[copyFrom];
copyFrom++;
copyTo++;
while ((str[copyFrom] == ' ') && (str[copyFrom] !='\0')) {
copyFrom++;
}
}
str[copyTo] = str[copyFrom];
if (str[copyTo] != '\0') {
copyFrom++;
copyTo++;
}
}
printf("Final String %s\n", str);
Hope it helps :-)

How to return empty string from a function in C?

How should I return an empty string from a function? I tried using lcp[i] = ' ' but it creates an error. Then I used lcp[i] = 0 and it returned an empty string. However, I do not know if it's right.
Also, is it necessary to use free(lcp) in the caller function? Since I could not free and return at the same time.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define MAX_LEN 50
char *find_LCP(char str1[], char str2[]);
char *find_LCP(char str1[], char str2[]){
char * lcp = malloc(MAX_LEN * sizeof(char));
int a = strlen(str1);
int b = strlen(str2);
int min = a < b ? a : b;
for(int i = 0; i < min; i++){
if(str1[i] == str2[i])
lcp[i] = str1[i];
else
lcp[i] = 0;
}
return lcp;
}
int main()
{
char str1[MAX_LEN], str2[MAX_LEN];
char * lcp;
printf("Enter first word > ");
scanf("%s", str1);
printf("Enter second word > ");
scanf("%s", str2);
lcp = find_LCP(str1, str2);
printf("\nLongest common prefix: '%s'\n", lcp);
free(lcp);
return 0;
}
An "empty" string is just a string with the first byte zero, so you can write:
s[0] = 0;
However, it is not clear what you are trying to do. The LCP of "foo" and "fob" is "fo", not the empty string.
You can also return as soon as you find the first non-matching character, no need to go until the end.
Further, you can simply pass the output string as a parameter and have lcp be an array. That way you avoid both malloc and free:
char lcp[MAX_LEN];
...
find_LCP(lcp, str1, str2);
If you want to empty a string without using a for loop then you can do
lcp[0] = 0
but for emptying a string it was right the way you did using a for loop.
There are plenty other ways of emptying the string word by word using for loop:
lcp[i] = '\0';
and it's the right way to make string empty as letter by letter you trying to do using for loop
But if you are not using some loops and simply empty a string then you can do this.
memset(buffer,0,strlen(buffer));
but this will only work for zeroing up to the first NULL character.
If the string is a static array, you can use:
memset(buffer,0,sizeof(buffer));
Your program has a bug: If you supply two identical strings, lcp[i] = 0; never executes which means that your function will return a string which is not NUL-terminated. This will cause undefined behvaior when you use that string in your printf in main.
The fix for this is easy, NUL-terminate the string after the loop:
int i;
for (i = 0; i < min; i++){
if(str1[i] == str2[i])
lcp[i] = str1[i];
else
break;
}
lcp[i] = 0;
As for the answer to the question, an empty string is one which has the NUL-terminator right at the start. We've already handled that as we've NUL-terminated the string outside the loop.
Also, is it necessary to use free(lcp) in the caller function?
In this case, it is not required as the allocated memory will get freed when the program exits, but I'd recommend keeping it because it is good practice.
As the comments say, you can use calloc instead of malloc which fills the allocated memory with zeros so you don't have to worry about NUL-terminating.
In the spirit of code golf. No need to calculate string lengths. Pick any string and iterate through it until the current character either null or differs from the corresponding character in the other string. Store the index, then copy appropriate number of bytes.
char *getlcp(const char *s1, const char *s2) {
int i = 0;
while (s1[i] == s2[i] && s1[i] != '\0') ++i;
char *lcp = calloc((i + 1), sizeof(*lcp));
memcpy(lcp, s1, i);
return lcp;
}
P.S. If you don't care about preserving one of input strings then you can simplify the code even further and just return the index (the position of the last character of the common prefix) from the function, then put '\0' at that index into one of the strings.

replacing "\"" by empty character

I need to replace " (ASCII value 34) char by empty character "".
In output, instead of quote i get an "?" question mark character.
I tried to use things like:
mystring[itit] = "";
mystring[itit] = '';
mystring[itit] = "\O";
My code:
strcpy( mystring ,op->data.value.str );
for(itit=0;itit<10;itit++)
{
if(mystring[itit] == 34)
{
mystring[itit] = NULL;
}
}
printf( "%s\n",mystring);
Any ideas how to fix that?
For clarification: the strings in mystring are like:
"hello"
"place "
"school"
all with the quotation marks - I Actually need to remove them and get:
hello
place
school
int removeChar(char *str, char c) {
int i, j;
for(i = 0, j = 0 ; str[i] ; i++){
if( str[i] == c) continue; // skip c do not copy it
str[j] = str[i]; // shift characters left
j++;
}
str[j]=0; // terminate the string
return j; // return the actual size
}
What you need to do is remove the character, not replace it, since you're not replacing it with anything. To do this, when you find the character is question, you need to move the remaining characters down.
int i,j;
strcpy(mystring, "aa\"bb\"cc");
for(i=0,j=0;i<10;i++)
{
if(mystring[i] != '"')
{
mystring[j] = mystring[i];
j++;
}
}
mystring[j] = '\0';
printf("mystring=%s\n",mystring);
Result:
mystring=aabbcc
To remove a character from a string, you can do this:
void remove(char* str, char rm)
{
char *src, *dst;
for (src = dst = str; *src != '\0'; ++src) {
*dst = *src;
if (*dst != rm) ++dst;
}
*dst = '\0'; /*insert terminator at the new place*/
}
and call with rm equal to 34.
This algorithm is well-known; I've adopted it from Kernighan & Ritchie. Do study it carefully with your debugger.
In C, strings are simply arrays of characters with a NUL (0) at the end. (They cannot contain NULs.) As with any array, you can't simply "remove" an element. You need to shift all the following elements one position, with the result that there will be an unneeded element at the end. With strings this extra element isn't a huge problem becauyse the NUL still identifies where the string ends.
In this case, you are copying the string first, so you might as well copy it without the characters you want to delete. Unless you know how many such characters there are, you will need to have allocated enough space in the new string for the entire string you want to copy:
/* Before this, you must ensure that mystring has enough space */
{
char* out = mystring;
const char* in = op->data.value.str;
do {
if (*in != '"') *out++ = *in;
} while (*in++);
}
Note: I use the fact that strings are NUL-terminated to terminate the loop, which saves me from having to know in advance how long op->data.value.str is. For this reason, I use character pointers rather than indexes.
There is no "empty character". A string can be empty by having no characters, but a character is an atomic element and can't be empty, like a box of apples can be empty, but one can't have an "empty apple".
Instead, you need to remove the quotes and close the space they took up. Better yet, if you do the copying yourself, just don't copy them:
char *psrc = op->data.value.str;
char *pdest = mystring;
while (*psrc != '\0')
{
if (*psrc != '\"')
{
*pdest = *psrc;
++pdest;
}
++psrc;
}
*pdest = '\0';
You can use this to strip all '\"'-characters:
void stripquotes(char *ptr) {
char *ptr2 = ptr;
do {
*ptr2 = *ptr++;
if (*ptr2 != '\"')
ptr2++;
} while (*ptr);
}

Printing a string due to a new line

Is there any efficient (- in terms of performance) way for printing some arbitrary string, but only until the first new line character in it (excluding the new line character) ?
Example:
char *string = "Hello\nWorld\n";
printf(foo(string + 6));
Output:
World
If you are concerned about performance this might help (untested code):
void MyPrint(const char *str)
{
int len = strlen(str) + 1;
char *temp = alloca(len);
int i;
for (i = 0; i < len; i++)
{
char ch = str[i];
if (ch == '\n')
break;
temp[i] = ch;
}
temp[i] = 0;
puts(temp);
}
strlen is fast, alloca is fast, copying the string up to the first \n is fast, puts is faster than printf but is is most likely far slower than all three operations mentioned before together.
size_t writetodelim(char const *in, int delim)
{
char *end = strchr(in, delim);
if (!end)
return 0;
return fwrite(in, 1, end - in, stdout);
}
This can be generalized somewhat (pass the FILE* to the function), but it's already flexible enough to terminate the output on any chosen delimiter, including '\n'.
Warning: Do not use printf without format specifier to print a variable string (or from a variable pointer). Use puts instead or "%s", string.
C strings are terminated by '\0' (NUL), not by newline. So, the functions print until the NUL terminator.
You can, however, use your own loop with putchar. If that is any performance penalty is to be tested. Normally printf does much the same in the library and might be even slower, as it has to care for more additional constraints, so your own loop might very well be even faster.
for ( char *sp = string + 6 ; *sp != '\0'; sp++ ) {
if ( *sp == '\n' ) break; // newline will not be printed
putchar(*sp);
}
(Move the if-line to the end of the loop if you want newline to be printed.)
An alternative would be to limit the length of the string to print, but that would require finding the next newline before calling printf.
I don't know if it is fast enough, but there is a way to build a string containing the source string up to a new line character only involving one standard function.
char *string = "Hello\nWorld\nI love C"; // Example of your string
static char newstr [256]; // String large enough to contain the result string, fulled with \0s or NULL-terimated
sscanf(string + 6, "%s", newstr); // sscanf will ignore whitespaces
sprintf(newstr); // printing the string
I guess there is no more efficient way than simply looping over your string until you find the first \n in it. As Olaf mentioned it, a string in C ends with a terminating \0 so if you want to use printf to print the string you need to make sure it contains the terminating \0 or yu could use putchar to print the string character by character.
If you want to provide a function creating a string up to the first found new line you could do something like that:
#include <stdio.h>
#include <string.h>
#define MAX 256
void foo(const char* string, char *ret)
{
int len = (strlen(string) < MAX) ? (int) strlen(string) : MAX;
int i = 0;
for (i = 0; i < len - 1; i++)
{
if (string[i] == '\n') break;
ret[i] = string[i];
}
ret[i + 1] = '\0';
}
int main()
{
const char* string = "Hello\nWorld\n";
char ret[MAX];
foo(string, ret);
printf("%s\n", ret);
foo(string+6, ret);
printf("%s\n", ret);
}
This will print
Hello
World
Another fast way (if the new line character is truly unwanted)
Simply:
*strchr(string, '\n') = '\0';

How does C know the end of my string?

I have a program in which I wanted to remove the spaces from a string. I wanted to find an elegant way to do so, so I found the following (I've changed it a little so it could be better readable) code in a forum:
char* line_remove_spaces (char* line)
{
char *non_spaced = line;
int i;
int j = 0;
for (i = 0; i <= strlen(line); i++)
{
if ( line[i] != ' ' )
{
non_spaced[j] = line[i];
j++;
}
}
return non_spaced;
}
As you can see, the function takes a string and, using the same allocated memory space, selects only the non-spaced characters. It works!
Anyway, according to Wikipedia, a string in C is a "Null-terminated string". I always thought this way and everything was good. But the problem is: we put no "null-character" in the end of the non_spaced string. And somehow the compiler knows that it ends at the last character changed by the "non_spaced" string. How does it know?
This does not happen by magic. You have in your code:
for (i = 0; i <= strlen(line); i++)
^^
The loop index i runs till strlen(line) and at this index there is a nul character in the character array and this gets copied as well. As a result your end result has nul character at the desired index.
If you had
for (i = 0; i < strlen(line); i++)
^^
then you had to put the nul character manually as:
for (i = 0; i < strlen(line); i++)
{
if ( line[i] != ' ' )
{
non_spaced[j] = line[i];
j++;
}
}
// put nul character
line[j] = 0;
Others have answered your question already, but here is a faster, and perhaps clearer version of the same code:
void line_remove_spaces (char* line)
{
char* non_spaced = line;
while(*line != '\0')
{
if(*line != ' ')
{
*non_spaced = *line;
non_spaced++;
}
line++;
}
*non_spaced = '\0';
}
The loop uses <= strlen so you will copy the null terminator as well (which is at i == strlen(line)).
You could try it. Debug it while it is processing a string containing only one space: " ". Watch carefully what happens to the index i.
How do you know that it "knows"? The most likely scenario is that you're simply having luck with your undefined behavior, and that there is a '\0'-character after the valid bytes of line end.
It's also highly likely that you're not seeing spaces at the end, which might be printed before hitting the stray "lucky '\0'".
A few other points:
There's no need to write this using indexing.
It's not very efficient to call strlen() on each loop iteration.
You might want to use isspace() to remove more whitespace characters.
Here's how I would write it, using isspace() and pointers:
char * remove_spaces(char *str)
{
char *ret = str, *put = str;
for(; *str != '\0'; str++)
{
if(!isspace((unsigned char) *str)
*put++ = *str;
}
*put = '\0';
return ret;
}
Note that this does terminate the space-less version of the string, so the returned pointer is guaranteed to point at a valid string.
The string parameter of your function is null-terminated, right?
And in the loop, the null character of the original string get also copied into the non spaced returned string. So the non spaced string is actually also null-terminated!
For your compiler, the null character is just another binary data that doesn't get any special treatment, but it's used by string APIs as a handy character to easily detect end of strings.
If you use the <= strlen(line), the length of the strlen(line) include the '\0' so your program can work. You can use debug and run analysis.

Resources