Originally Posted by
yuxuantim
migf1 OK I like your idea best. Any ideas to get me started on my own library. I will make use of your code For the time being I did an easy fix. I made a holder string and sprintf a char or int into it then I strcat it onto my string. I now am wanting to separate a string on the spaces…
A couple of years ago I had started a simple string library which was mostly operating on statically allocated string (meaning that most of them expect the size of the strings as argument).
Unfortunately I've never finished it. However, its documentation can be found online, here. After the description of each function you'll find a line like the following:
Definition at line
117 of file
s2.c.
By clicking on the line number you can see the code of that particular function.
It seems that functions like: s_cappend(), s_tokenize() / s_tokenize_safe() and/or s_strtok() / s_strtok_r() may be useful for what you are after. Feel free to "steal" their code if you think it will help you.
On the other hand, functions like the ones I've posted previously in this thread, operate on dynamically allocated c-strings, where the end programmer does not have to deal with lengths at all.
But the code I posted in the previous post is not optimal. For example, you can save lots of realloc()s (it is a costly function) if you define a String data-type, which along the c-string data it will also hold the size of the c-string. During construction you can allocate it to a predefined number of bytes, and you will double it only when the String needs to grow beyond that.
For example, for the s_append() function, you wouldn't have to realloc the destination string if 1 + strlen(dst) + strlen(src) was less that the currently (pre)allocated size of dst.
This kind of low-level housekeeping may become quite tedious and error-prone (especially if you are not familiar with the language).
That's why using an already tested string library, like the ones we've already pointed out in the thread, can save you lots of griff and frustration
That being said, trying to write your own simple string library (for either static or dynamic strings, or even both), will certainly help you familiarize yourself with the language. It will be a nice learning experience, imo.
You may start by improving the efficiency of the functions s_append() and s_append_ntimes() as described above.
Writing a tokenizer using strtok() is not that difficult either (you may want to have a look at the code of the functions s_stokenize() / s_tokenize_safe() to get some ideas... or even better, look at the code of well established string-libraries, like the ones we have already suggested).
Regarding sprintf(), if you like dynamically allocated strings better, glib provides three relative functions: g_strdup_printf(), g_strdup_vprintf() and g_vasprintf()
I haven't looked at GLib's code, but I guess a simple implementation of the former two, could be something along the following lines:
Code:
/* --------------------------------------------------------------
* char *s_dup_vprintf():
*
* This function is a wrapper to ISO C99's vsnprintf(). It creates
* dynamically the buffer needed to hold the printed output, and if
* it succeeds it returns a pointer to it. On error, it returns NULL.
*
* NOTES:
* 1. vargs MUST have been already initialized in the caller
* (by the va_start macro, and possibly subsequent va_arg
* calls).
* 2. The returned buffer, if non-NULL, MUST be freed in the
* calling environment
* --------------------------------------------------------------
*/
char *s_dup_vprintf( const char *fmt, va_list vargs )
{
int nchars = 0;
char *buf = NULL;
size_t bufsz = BUFSIZ;
/* make buf to hold the text and pass it to vsnprintf */
if ( NULL == (buf = calloc(1, bufsz)) ) {
fprintf( stderr, "%s: calloc failed!\n", __func__ );
goto ret_failure;
}
nchars = vsnprintf( buf, bufsz, fmt, vargs );
if ( nchars < 0 ) {
fprintf( stderr, "%s: vsnprintf failed!\n", __func__ );
goto ret_failure;
}
/* Was buf too small to hold the text?
* Reallocate 1+nchars bytes and re-apply.
*/
if ( nchars >= (int)bufsz ) {
char *try = NULL;
bufsz = 1 + nchars; /* for the NUL byte */
try = realloc( buf, bufsz );
if ( NULL == try ) {
fprintf( stderr, "%s: realloc failed!\n", __func__ );
goto ret_failure;
}
buf = try;
nchars = vsnprintf( buf, bufsz, fmt, vargs );
if ( nchars < 0 ) {
fprintf( stderr, "%s: 2nd vsnprintf failed!\n", __func__ );
goto ret_failure;
}
}
return buf;
ret_failure:
if ( buf ) {
free( buf );
}
return NULL;
}
/* --------------------------------------------------------------
* char *s_dup_printf():
*
* This function is similar to ISO C99's snprintf() but it creates
* dynamically the string needed to hold the print-out. It returns
* the string, or NULL on error.
* --------------------------------------------------------------
*/
char *s_dup_printf( const char *fmt, ... )
{
char *txtout = NULL;
va_list vargs;
va_start( vargs, fmt );
txtout = s_dup_vprintf( fmt, vargs );
va_end( vargs );
return txtout;
}
So you can, for example...
Code:
...
int n = 10;
char *temp = NULL;
s_append(
&sarr[0],
temp = s_dup_printf( "%d", n )
);
free(temp);
...
without worrying for the length of temp.