Replace a string within a C char string (strreplace, no sprintf) - c

Hello and sorry if this questions is already answered (in the way I want to :-) ) but I think I have a memory problem.
Assuming the following C function (yes, this one is very dirty and not optimized):
char *strreplace(char **str, char *dst, char *replace) {
int replacestart = stringcompare(*str, dst), replaceend = strlen(dst), replacelen = strlen(replace);
char *tmp1 = (char *)malloc(sizeof(char) * (replacestart + 1)), *tmp2 = (char *)malloc(sizeof(char) * ((strlen(*str) - replaceend) + 1));
memset(tmp1, 0, sizeof(char) * (replacestart + 1));
memset(tmp2, 0, sizeof(char) * ((strlen(*str) - replaceend) + 1));
strncpy(tmp1, *str, replacestart);
strncpy(tmp2, *str + replacestart + replaceend, (strlen(*str) - (replaceend + replacestart)));
*str = realloc(*str, strlen(tmp1) + replacelen + strlen(tmp2));
memset(*str, 0, strlen(tmp1) + replacelen + strlen(tmp2));
strncpy(*str, tmp1, strlen(tmp1));
strncpy(*str + strlen(tmp1), replace, replacelen);
strncpy(*str + strlen(tmp1) + replacelen, tmp2, strlen(tmp2));
return *str;
}
As seen, it should replace *dst with *replace whithin **str.
This works basically as expected. Here's the problem:
The output (*str) is not cleard out with zeros after memsetting it and has still the wrong length, even after reallocating.
Means, if the string is longer after replacing, the last chars are cutted of *str.
In turn, if the string is shorter, old chars are at the end of the char string are found.
What did I wrong. Mentioned that I don't want to use sprintf and no C++-STL, just want to do this with pointers in C.
Other words, what would be the right way to replace a string in an c char string with pointers.
Thanks alot.
EDIT after reqeuest for more information
I use this function as following:
...open a textfile via FILE type, determining file lengts (fseek)...
char *filecontent = (char *)malloc(sizeof(char) * filesize);
if(filesize != fread(filecontent, sizeof(char), filesize, fd)) {
free(filecontent);
return -1;
}
streplace(&filecontent, "[#TOBEREPLACED#]", "replaced");
...doing other stuff with filecontent...
EDIT 2, adding stringcompare()
int stringcompare(const char *string, const char *substr) {
int i, j, firstOcc;
i = 0, j = 0;
while(string[i] != '\0') {
while(string[i] != substr[0] && string[i] != '\0') {
i++;
}
if(string[i] == '\0') {
return -1;
}
firstOcc = i;
while(string[i] == substr[j] && string[i] != '\0' && substr[j] != '\0') {
i++;
j++;
}
if(substr[j] == '\0') {
return firstOcc;
}
if(string[i] == '\0') {
return -1;
}
i = firstOcc + 1;
j = 0;
}
}

The logic of the function is not simple as it seems at the first glance.
You should decide what action to execute 1) if the source string is empty and 2) if the destination string is empty.
For example if the destination string is empty then the standard function strstr will return the address of the first character of the source string. However logically if dst is an empty string then the source string should not be changed except the case when it is in turn an empty string. In this case the function should just create a new string equal to the string replace.
Take into account that as the strings dst and replace are not changed within the function they should be declared with the qualifier const.
char * strreplace( char **str, const char *dst, const char *replace );
Also the function should report whether a new memory allocation was successful within the function by returning a null pointer in case when the allocation failed.
Taking all this into account the function can look the following way as it is shown in the demonstrative program.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
char * strreplace( char **str, const char *dst, const char *replace )
{
char *result = *str;
if ( *str[0] == '\0' )
{
if ( dst[0] == '\0' && replace[0] != '\0' )
{
char *result = malloc( strlen( replace ) + 1 );
if ( result )
{
strcpy( result, replace );
free( *str );
*str = result;
}
}
}
else if ( dst[0] != '\0' )
{
char *pos = strstr( *str, dst );
if ( pos != NULL )
{
size_t dst_n = strlen( dst );
size_t replace_n = strlen( replace );
result = ( char * )malloc( strlen( *str ) - dst_n + replace_n + 1 );
if ( result )
{
size_t n = pos - *str;
strncpy( result, *str, n );
strcpy( result + n, replace );
strcpy( result + n + replace_n, *str + n + dst_n );
}
free( *str );
*str = result;
}
}
return result;
}
int main(void)
{
const char *hello = "Hello everybody here";
char *str = malloc( sizeof( char ) );
str[0] = '\0';
if ( strreplace( &str, "", hello ) )
{
puts( str );
}
if ( strreplace( &str, "everybody", "Eurobertics" ) )
{
puts( str );
}
if ( strreplace( &str, "Hello", "Bye" ) )
{
puts( str );
}
if ( strreplace( &str, " here", "" ) )
{
puts( str );
}
if ( strreplace( &str, "Bye ", "" ) )
{
puts( str );
}
free( str );
return 0;
}
The program output is
Hello everybody here
Hello Eurobertics here
Bye Eurobertics here
Bye Eurobertics
Eurobertics

Your function looks overly complex. Here is a simple, working version:
char *strreplace(char **str, char *dst, char *replace) char *strreplace(char **str, char *dst, char *replace)
{
char *start, *tmp;
int n;
if ((start=strstr(*str,dst))==0) return(0);
n= (start-*str) + strlen(start+strlen(dst)) + strlen(replace) + 1;
tmp=malloc(n);
strncpy(tmp,*str,start-*str);
strcpy(tmp+(start-*str),replace);
strcat(tmp,start+strlen(dst));
free(*str);
*str= tmp;
return(tmp);
}
With test function:
void reptest(void)
{
char *src;
char rep[]= "brave new world";
src=malloc(strlen("Hello world of wonders")+1);
strcpy(src,"Hello world of wonders");
strreplace(&src,"world",rep);
printf("%s\n", src);
free(src);
}
Edit: my previous version forgot to copy the remainder. Fixed.

Related

Can't print string after reverse in C

I'm writing this function to return a char pointer of reversed string.
void * PreverseStr (char str[])
{
int size = strlen (str);
char *returnstr = (char *)malloc (size * sizeof(char));
for (int i = size - 1; i >= 0 ; i--)
{
*returnstr = str[i];
returnstr ++;
}
returnstr = 0;
returnstr -= size;
return returnstr ;
}
To test this function I wrote a main function like this
int main()
{
char str[] = "abcdefghijklmnopqrstuvwxyz";
char *newstr = PreverseStr(str);
printf("Reversed string using pointer: %s\n", newstr);
free(newstr);
return 0;
}
But it crashes before it could print out anything. I wonder what's wrong with my code. It would be much helpful if you can explain a fix to this.
For starters the return type void * makes no sense. The return type should be char *. As the function creates a new string without changing the source string then the function parameter should have the qualifier const.
This memory allocation
char *returnstr = (char *)malloc (size * sizeof(char));
allocates not enough space tp store the terminating zero character '\0' of the source string.
You need to write at least
char *returnstr = (char *)malloc ( ( size + 1 ) * sizeof(char));
After the for loop the pointer returnstr points to beyond the allocated memory because it is increased within the loop
returnstr ++;
Moreover after this assignment
returnstr = 0;
it becomes a null pointer.
The function can be declared and defined the following way
char * reverse_copy( const char s[] )
{
size_t n = strlen( s );
char *p = malloc( n + 1 );
if ( p != NULL )
{
p += n;
*p = '\0';
while ( n-- )
{
*--p = *s++;
}
}
return p;
}
Here is a demonstration program.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
char *reverse_copy( const char s[] )
{
size_t n = strlen( s );
char *p = malloc( n + 1 );
if (p != NULL)
{
p += n;
*p = '\0';
while (n--)
{
*--p = *s++;
}
}
return p;
}
int main( void )
{
const char *s = "Hello, World!";
puts( s );
char *p = reverse_copy( s );
if (p) puts( p );
free( p );
}
Its output is
Hello, World!
!dlroW ,olleH

when printing a char* in C only the first character is being printed

I'm trying to write a function that will print out a substring of string however when printing it, only the first character in the array is printed.
As you can see in the code I've put a printf statement in the function after the substring is created and it displays properly. However when the function is passed into a printf function in the main function it only prints the first character.
Thanks for any help people are able to provide.
#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
char *ft_substr (const char *s, unsigned int start, size_t len)
{
char *str;
unsigned int i;
unsigned int j;
str = malloc(len * sizeof(char) + 1);
i = start;
j = 0;
while (i < len + start)
{
str[j] = s[i];
j++;
i++;
}
str[j + 1] = '\0';
printf("%s\n", str);
free(str);
return (str);
}
int main (void)
{
char hello[] = "Hello World";
char sub = *ft_substr(hello, 1, 4);
printf("%s\n", &sub);
return (0);
}
Before returning the pointer str from the function you freed all the allocated memory
free(str);
return (str);
So the returned pointer is invalid.
Remove the statement
free(str);
Another problem in the function is using an incorrect index in this statement
str[j + 1] = '\0';
Just write
str[j] = '\0';
Also this declaration is incorrect
char sub = *ft_substr(hello, 1, 4);
It declares a single character while you need to declare a pointer that will point to the dynamically allocated string in the function. So write
char *sub = ft_substr(hello, 1, 4);
and then write
printf("%s\n", sub);
free( sub );
And if you are using the type size_t for the length of a string then use indices also of the type size_t.
Here is a demonstrative program.
#include <stdio.h>
#include <stdlib.h>
char * ft_substr( const char *s, size_t start, size_t len )
{
char *str = malloc( len + 1 );
if ( str != NULL )
{
size_t i = 0;
for ( ; i < len; i++ )
{
str[i] = s[start + i];
}
str[i] = '\0';
}
return str;
}
int main(void)
{
const char *hello= "Hello World";
char *sub = ft_substr( hello, 1, 4 );
if ( sub != NULL ) puts( sub );
free( sub );
return 0;
}
Its output is
ello
The function will be more safer if it will check whether the starting index and the length are specified correctly. For example
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
char * ft_substr( const char *s, size_t start, size_t len )
{
char *str = NULL;
size_t n = strlen( s );
if ( start < n )
{
if ( n - start < len ) len = n - start;
str = malloc( len + 1 );
if ( str )
{
memcpy( str, s + start, len );
str[len] = '\0';
}
}
return str;
}
int main(void)
{
const char *hello= "Hello World";
char *sub = ft_substr( hello, 1, 4 );
if ( sub != NULL ) puts( sub );
free( sub );
return 0;
}
You have UB in your main. Yout try to print as string a single character (passed as reference).
int main (void)
{
char hello[] = "Hello World";
char *sub = ft_substr(hello, 1, 4);
printf("%s\n", sub?sub:"");
free(sub);
return (0);
}

char* gets corrupted on the final iteration of a loop

So, I'm trying to build a string_split function to split a c-style string based on a delimiter.
Here is the code for the function:
char** string_split(char* input, char delim)
{
char** split_strings = malloc(sizeof(char*));
char* charPtr;
size_t split_idx = 0;
int extend = 0;
for(charPtr = input; *charPtr != '\0'; ++charPtr)
{
if(*charPtr == delim || *(charPtr+1) == '\0')
{
if(*(charPtr+1) == '\0') extend = 1; //extend the range by one for the null byte at the end
char* string_element = calloc(1, sizeof(char));
for(size_t i = 0; input != charPtr+extend; ++input, ++i)
{
if(string_element[i] == '\0')
{
//allocate another char and add a null byte to the end
string_element = realloc(string_element, sizeof(char) * (sizeof(string_element)/sizeof(char) + 1));
string_element[i+1] = '\0';
}
string_element[i] = *input;
}
printf("string elem: %s\n", string_element);
split_strings[split_idx++] = string_element;
//allocate another c-string if we're not at the end of the input
split_strings = realloc(split_strings, sizeof(char*) *(sizeof(split_strings)/sizeof(char*) + 1));
//skip over the delimiter
input++;
extend = 0;
}
}
free(charPtr);
free(input);
return split_strings;
}
Essentially, the way it works is that there are two char*, input and charPtr. charPtr counts up from the start of the input string the the next instance of the delimiter, then input counts from the previous instance of the delimiter (or the start of the input string), and copies each char into a new char*. once the string is built it is added to a char** array.
There are also some twiddly bits for skipping over delimiters and dealing with the end points of the input string. the function is used as so:
int main()
{
char* str = "mon,tue,wed,thur,fri";
char delim = ',';
char** split = string_split(str, delim);
return 1;
}
Anyway, it works for the most part, except the first char* in the returned char** array is corrupted, and is just occupied by random junk.
For example printing the elements of split from main yields:
split: α↨▓
split: tue
split: wed
split: thur
split: fri
Whats odd is that the contents of split_strings[0], the array of char* which returns the desired tokens is mon, as it should be for this example, right up until the final loop of the main for-loop in the string_split function, specifically its the line:
split_strings[split_idx++] = string_element;
which turns its contents from mon to Junk. Any help is appreciated, thanks.
Your function is incorrect at least because it tries to free the passed string
char** string_split(char* input, char delim)
{
//...
free(charPtr);
free(input);
return split_strings;
}
that in the case of your program is a string literal
char* str = "mon,tue,wed,thur,fri";
char delim = ',';
char** split = string_split(str, delim);
You may not free a string literal.
And the first parameter shall have the qualifier const.
There are numerous other errors in your function.
For example the expression sizeof(string_element)/sizeof(char) used in this statement
string_element = realloc(string_element, sizeof(char) * (sizeof(string_element)/sizeof(char) + 1));
does not yield the number of characters early allocated to the array pointed to by the pointer string_element. And there is no great sense to reallocate the array for each new character.
The function can look for example the following way as it is shown in the demonstrative program below.
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
char ** string_split( const char *s, char delim )
{
size_t n = 1;
char **a = calloc( n, sizeof( char * ) );
while ( *s )
{
const char *p = strchr( s, delim );
if ( p == NULL ) p = s + strlen( s );
if ( p != s )
{
char *t = malloc( p - s + 1 );
if ( t != NULL )
{
memcpy( t, s, p - s );
t[p-s] = '\0';
}
char **tmp = realloc( a, ( n + 1 ) * sizeof( char * ) );
if ( tmp == NULL )
{
free( t );
break;
}
a = tmp;
a[n-1] = t;
a[n] = NULL;
++n;
}
s = p + ( *p != '\0' );
}
return a;
}
int main(void)
{
char* str = "mon,tue,wed,thur,fri";
char delim = ',';
char **split = string_split( str, delim );
for ( char **p = split; *p != NULL; ++p )
{
puts( *p );
}
for ( char **p = split; *p != NULL; ++p ) free( *p );
free( split );
return 0;
}
The program output is
mon
tue
wed
thur
fri
The final function for anyone wonder, should be pretty fool proof.
char** string_split(char* input, char delim, bool skip_delim)
{
assert(*input != '\0');
char** split_strings = malloc(sizeof(char*));
char* charPtr;
size_t split_idx = 0;
size_t num_allocated_strings = 1;
size_t extend = 0;
for(charPtr = input; *charPtr != '\0'; ++charPtr)
{
if(*charPtr == delim || *(charPtr+1) == '\0')
{
if(*(charPtr+1) == '\0') extend = 1; //extend the range by one for the null byte at the end
char* string_element = calloc(1, sizeof(char));
for(size_t i = 0; input != charPtr+extend; ++input, ++i)
{
if(string_element[i] == '\0')
{
//allocate another char and add a null byte to the end
char* temp = realloc(string_element, sizeof(char) * (strlen(string_element) + 1));
if(temp == NULL)
{
free(string_element);
free(split_strings);
break;
}
string_element = temp;
string_element[i+1] = '\0';
}
string_element[i] = *input;
}
split_strings[split_idx++] = string_element;
num_allocated_strings++;
//allocate another c-string
char** temp = realloc(split_strings, sizeof(char*) * num_allocated_strings);
if(temp == NULL)
{
free(string_element);
free(split_strings);
break;
}
split_strings = temp;
//skip over the delimiter if required
if(skip_delim) input++;
extend = 0;
}
}
split_strings[num_allocated_strings-1] = NULL;
return split_strings;
}

Breaking up a string into a list of tokens using another string as a delimiter?

Let's say I have this string:
char *myTestString = "Hello AND test AND test2";
I want to break this down into the set { Hello, test, test2 }, which I can finally iterate over.
Or I have
char *myTestString2 = "Hi AND there AND test AND test2";
I want to break this down into the set { Hi, there, test, test2 }, which I can later iterate over.
How do I achieve this using C?
EDIT:
Another example is splitting "Hello there AND test" should give out set { Hello there, test }.
For clarification "AND" is delimiter here.
When code does not want to alter the source string, use strcspn(s, delimet) to look for initial portion of a s not make up of delimit. It returns the offset.
Use strspn(s, delimet) to look for initial portion of a s made up of delimit. It returns the offset.
Note: As others mentioned, strtok() is no good for string literals, and in this case you should go with Chux's answer (strcspn), but if that's not an issue and you can work with strings stored in arrays, then continue reading. A last resort effort would be to work with a copy of the string literal.
First, you have to decide which data structure you'll use for your set (e.g. a simple linked list, where you check before instertion for duplicates).
Then, use strtok() to your string, and if the current token is different than "AND" (or any other string to be ignored - you'd have an ignore set of strings as well), then insert it in the set, otherwise, continue to the next token.
Here is a basic Complete Minimal Example to get you started:
#include <stdio.h>
#include <string.h>
#define N 3 // Max size of set
#define LEN 32 // Max length of word - 1
int main ()
{
char set[N][LEN] = {0};
char* ignore_str = "AND";
char str[] ="Hello AND test AND test2";
char* pch;
printf ("Splitting string \"%s\" into tokens:\n",str);
pch = strtok (str," ");
int i = 0;
while (pch != NULL)
{
printf ("%s\n",pch);
if(strcmp(pch, ignore_str))
strcpy(set[i++], pch);
pch = strtok (NULL, " ");
}
printf("My set is: {");
for(int j = 0; j < i; ++j)
printf("%s, ", set[j]);
printf("}\n");
return 0;
}
Output:
Splitting string "Hello AND test AND test2" into tokens:
Hello
AND
test
AND
test2
My set is: {Hello, test, test2, }
Here, I used an array to represent the set, assuming that the maximum size of the set would be 3. Of course, you could use more dynamic approach instead (e.g. dynamic memory allocated array or list).
Here you are.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
char ** split( const char *s1, const char *s2 )
{
char **tokens = malloc( sizeof( char * ) );
int success = tokens != NULL;
if ( success )
{
const char *delim = " \t";
*tokens = NULL;
for ( size_t n = 1, len = strlen( s2 ); success && *s1; )
{
s1 += strspn( s1, delim );
if ( *s1 )
{
const char *p = s1;
s1 += strcspn( s1, delim );
if ( strncmp( p, s2, len ) != 0 )
{
char **tmp = realloc( tokens, ( n + 1 ) * sizeof( char * ) );
if ( ( success = tmp != NULL ) )
{
tokens = tmp;
success = ( tokens[n-1] = calloc( 1, s1 - p + 1 ) ) != NULL;
strncpy( tokens[n-1], p, s1 - p );
tokens[n] = NULL;
++n;
}
if ( !success )
{
for ( size_t i = 0; i < n; i++ ) free( tokens[i] );
free( tokens );
}
}
}
}
}
return tokens;
}
int main(void)
{
const char *s1 = "Hi AND there AND test AND test2";
const char *s2 = "AND";
char **tokens = split( s1, s2 );
if ( tokens != NULL )
{
for ( char **p = tokens; *p != NULL; ++p )
{
puts( *p );
}
char **p = tokens;
do
{
free( *p );
} while ( *p++ != NULL );
free( tokens );
}
return 0;
}
The program output is
Hi
there
test
test2
The function returns NULL if a memory allocation was not successful. Otherwise it returns a pointer to an array of the element type char * the last element of which is null pointer.
The words in the source string are splited by tabs and spaces. You can change the delimiters as you like.
After your comment to my previous solution it seems you need the following
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
char ** split( const char *s1, const char *s2 )
{
char **tokens = malloc( sizeof( char * ) );
int success = tokens != NULL;
if ( success )
{
const char *delim = " \t";
*tokens = NULL;
for ( size_t n = 1, len2 = strlen( s2 ); success && *s1; )
{
for ( int empty = 1; empty; )
{
s1 += strspn( s1, delim );
if ( ( empty = strncmp( s1, s2, len2 ) == 0 ) )
{
s1 += len2;
}
}
if ( *s1 )
{
const char *p = strstr( s1, s2 );
size_t len1 = p == NULL ? strlen( s1 ) : p - s1;
char **tmp = realloc( tokens, ( n + 1 ) * sizeof( char * ) );
if ( ( success = tmp != NULL ) )
{
tokens = tmp;
success = ( tokens[n-1] = calloc( 1, len1 + 1 ) ) != NULL;
strncpy( tokens[n-1], s1, len1 );
tokens[n] = NULL;
++n;
s1 += p == NULL ? len1 : len1 + len2;
}
if ( !success )
{
for ( size_t i = 0; i < n; i++ ) free( tokens[i] );
free( tokens );
}
}
}
}
return tokens;
}
int main(void)
{
const char *s1 = "Hi there AND test test2";
const char *s2 = "AND";
char **tokens = split( s1, s2 );
if ( tokens != NULL )
{
for ( char **p = tokens; *p != NULL; ++p )
{
puts( *p );
}
char **p = tokens;
do
{
free( *p );
} while ( *p++ != NULL );
free( tokens );
}
return 0;
}
The program output is
Hi there
test test2
Maybe you need also to remove trailing blanks of a extracted sub-string that I hope you can do yourself.:).
strstr can be used to locate the sub-string. Check that the leading and trailing characters are space or trailing terminating zero.
As needed remove whitespace.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
int main ( void) {
char *myTestString = " AND SANDY AND Hello there AND AND test AND test2 AND test3 ";
char *match = "AND";
char *first = myTestString;
char *start = myTestString;
char *find = myTestString;
int len = strlen ( match);
while ( isspace ( (unsigned char)*start)) {//skip leading whitespace
++start;
++first;
}
while ( ( find = strstr ( start, match))) {
if ( find != first) {
//check for leading and trailing space or terminating zero
while ( ! (isspace ( (unsigned char)*(find - 1))
&& ( isspace ( (unsigned char)*(find + len)) || 0 == *(find + len)))) {
find = strstr ( find + 1, match);
if ( ! find) {
find = start + strlen ( start);
while ( isspace ( (unsigned char)*(find - 1))) {
--find;
}
break;
}
}
int span = (int)(find - start);
if ( span) {
printf ( "%.*s\n", span, start);
}
}
start = find + strlen ( match);
while ( isspace ( (unsigned char)*start)) {//skip trailing whitespace
++start;
}
}
if ( *start) {
int end = strlen ( start) - 1;
while ( isspace ( (unsigned char)start[end])) {
--end;//remove trailing whitspace
}
printf ("%.*s\n", end + 1, start);
}
return 0;
}
Allocate memory to char**, allocate memory and copy each token.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
char **freetokens ( char **tokens);
void showtokens ( char **tokens);
char **addtoken ( char **tokens, int *count, char *text, int size);
int main ( void) {
char *myTestString = " AND SANDY AND Hello there test AND AND test2 AND test3 ";
char *match = "AND";
char *first = myTestString;
char *start = myTestString;
char *find = myTestString;
char **tokens = NULL;
int items = 0;
int len = strlen ( match);
while ( isspace ( (unsigned char)*start)) {//skip leading whitespace
++start;
++first;
}
while ( ( find = strstr ( start, match))) {
if ( find != first) {
//check for leading and trailing space or terminating zero
while ( ! (isspace ( (unsigned char)*(find - 1))
&& ( isspace ( (unsigned char)*(find + len)) || 0 == *(find + len)))) {
find = strstr ( find + 1, match);
if ( ! find) {
find = start + strlen ( start);
while ( isspace ( (unsigned char)*(find - 1))) {
--find;//remove trailing whitespace
}
break;
}
}
int span = (int)(find - start);
if ( span) {
tokens = addtoken ( tokens, &items, start, span);
}
}
start = find + strlen ( match);
while ( isspace ( (unsigned char)*start)) {//skip trailing whitespace
++start;
}
}
if ( *start) {
int end = strlen ( start);
while ( isspace ( (unsigned char)start[end - 1])) {
--end;
}
tokens = addtoken ( tokens, &items, start, end);
}
showtokens ( tokens);
tokens = freetokens ( tokens);
return 0;
}
char **addtoken ( char **tokens, int *count, char *text, int size) {
char **temp = NULL;
if ( NULL == ( temp = realloc ( tokens, sizeof *tokens * ( *count + 2)))) {
fprintf ( stderr, "problem realloc tokens\n");
return tokens;
}
tokens = temp;
tokens[*count + 1] = NULL;//sentinel
if ( NULL == ( tokens[*count] = malloc ( size + 1))) {
fprintf ( stderr, "problem realloc tokens[]\n");
return tokens;
}
memmove ( tokens[*count], text, size);
tokens[*count][size] = 0;//terminate
++*count;
return tokens;
}
char **freetokens ( char **tokens) {
int each = 0;
while ( tokens && tokens[each]) {
free ( tokens[each]);
++each;
}
free ( tokens);
return NULL;
}
strstr() is the tool you are looking for. It can locate a string inside another string.
Here is a simple solution with these extra specifications:
the return value is an array of (n+1) entries, the last one being a null pointer.
the separator string can appear anywhere, including inside a word.
substrings are trimmed: initial and trailing white space is removed
substrings are allocated with strndup() which is standardized in POSIX.
the separator string must have a length of at least 1
#include <ctype.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
char *strdup_trim(const char *s, size_t n) {
while (n > 0 && isspace((unsigned char)*s)) {
s++;
n--;
}
while (n > 0 && isspace((unsigned char)s[n - 1])) {
n--;
}
return strndup(s, n);
}
char **split(const char *str, const char *sep) {
size_t i, n, sep_len = strlen(sep);
char **a;
const char *p, *p0;
if (sep_len == 0)
return NULL;
for (n = 0, p = str; (p = strstr(p, sep)) != NULL; n++, p += sep_len)
continue;
a = malloc(sizeof(*a) * (n + 2));
if (a == NULL)
return NULL;
for (i = 0, p = str; (p = strstr(p0 = p, sep)) != NULL; i++, p += sep_len) {
a[i] = strdup_trim(p0, p - p0);
}
a[i++] = strdup_trim(p0, strlen(p0));
a[i] = NULL;
return a;
}
void free_split(char **a) {
if (a) {
for (size_t i = 0; a[i]; i++)
free(a[i]);
free(a);
}
}
void test(const char *str, const char *sep) {
char **a = split(str, sep);
printf("split('%s', '%s') -> {", str, sep);
for (size_t i = 0; a[i]; i++)
printf("%s '%s'", &","[!i], a[i]);
printf(" }\n");
free_split(a);
}
int main() {
test("Hello AND test AND test2", "AND");
test("Hi AND there AND test AND test2", "AND");
test("Hello there AND test", "AND");
return 0;
}
Output:
split('Hello AND test AND test2', 'AND') -> { 'Hello', 'test', 'test2' }
split('Hi AND there AND test AND test2', 'AND') -> { 'Hi', 'there', 'test', 'test2' }
split('Hello there AND test', 'AND') -> { 'Hello there', 'test' }

Why doesn't pointer arithmetic work in char* functions

THE MESSAGE:
/usr/local/webide/runners/c_runner.sh: line 54: 20533 Segmentation fault
nice -n 10 valgrind --leak-check=full --log-file="$valgrindout" "$exefile"
I can't understand why I can't use pointer arithmetic when my function type is not void. Take a look at this example:
Let's say I have to write a function that would 'erase' all whitespaces before the first word in a string.
For example, if we had a char array:
" Hi everyone"
it should produce "Hi everyone" after the function's modification.
Here is my code which works fine when instead of
char* EraseWSbeforethefirstword() I have
void EraseWSbeforethefirstword.
When the function returns an object char* it can't even be compiled.
char* EraseWSbeforethefirstword(char *s) {
char *p = s, *q = s;
if (*p == ' ') { /*first let's see if I have a string that begins with a space */
while (*p == ' ') {
p++;
} /*moving forward to the first non-space character*/
while (*p!= '\0') {
*q = *p;
p++;
q++;
} /*copying the text*/
*q = '\0'; /*If I had n spaces at the beginning the new string has n characters less */
}
return s;
}
Here is a function implementation that has the return type char * as you want.
#include <stdio.h>
char * EraseWSbeforethefirstword( char *s )
{
if ( *s == ' ' || *s == '\t' )
{
char *p = s, *q = s;
while ( *p == ' ' || *p == '\t' ) ++p;
while ( ( *q++ = *p++ ) );
}
return s;
}
int main(void)
{
char s[] = "\t Hello World";
printf( "\"%s\"\n", s );
printf( "\"%s\"\n", EraseWSbeforethefirstword( s ) );
return 0;
}
The program output is
" Hello World"
"Hello World"
Take into account that you may not modify string literals. So the program will have undefined behavior if instead of the array
char s[] = "\t Hello World";
there will be declared a pointer to a string literal
char *s = "\t Hello World";
If you want that the function could deal with string literals then the function has to allocate a new array dynamically and to return a pointer to its first element.
If you may not use standard C string functions then the function can look the following way
#include <stdio.h>
#include <stdlib.h>
char * EraseWSbeforethefirstword( const char *s )
{
size_t blanks = 0;
while ( s[blanks] == ' ' || s[blanks] == '\t' ) ++blanks;
size_t length = 0;
while ( s[length + blanks] != '\0' ) ++length;
char *p = malloc( length + 1 );
if ( p != NULL )
{
size_t i = 0;
while ( ( p[i] = s[i + blanks] ) != '\0' ) ++i;
}
return p;
}
int main(void)
{
char *s= "\t Hello World";
printf( "\"%s\"\n", s );
char *p = EraseWSbeforethefirstword( s );
if ( p ) printf( "\"%s\"\n", p );
free( p );
return 0;
}

Resources