I have a char array that i get from serial port (Arduino) .
the data is separated by commas so its like this :
header:data
The chars array that holds this structure is pre defined like this : char content[50];
I am trying to write a function , that will get a parameter content
and return only the header , and then another function to return only the data. I know i have to start with this - but dont really know how to continue:
void getHeader( char* localString)
{
char delimiters[] = ":";
char *valPosition=NULL;
char newString=NULL ;
valPosition = strtok(localString, delimiters); //what is this doing ?
.... //whats now ?
//also how do you return the header to the argument
I have tried that according to an answer here ,but i get nothing to print:
char delimiters[] = ":";
char *valPosition=NULL;
char newString=NULL ;
char * header = NULL;
valPosition = strtok(content, delimiters);
malloc(strlen(valPosition) + 1);
strcpy(header, valPosition);
Serial.println(header);
Let's have a look at the man page for strtok(). It says
char *strtok(char *str, const char *delim);
The strtok() function parses a string into a sequence of tokens..... The delim argument specifies a set of bytes that delimit the tokens in the parsed string. ...... Each call to strtok() returns a pointer to a null-terminated string containing the next token.
That means, when you call
valPosition = strtok(localString, delimiters); /
strtok() will search localString for the delimiter specified in delimiters
and if it finds any, it will return the token as a null-terminated string.
Beware, strtok()
modify their first argument.
and
cannot be used on constant strings.
so, the localString should be modifiable, i.e., cannot be a string literal.
Next, as per your format, strtok() will return the header, without the :.
so, you need to copy the returned string into another and return that. You can use dynamic memory allocation, as per following algorithm
Define a char * header = NULL;
Check the returned value of strtok(), if not NULL, allocate memory to header, like header = malloc(strlen(valPosition) + 1);
copy the data using strcpy(header, valPosition);
return the header from the function.
I hope, you understand that you need to change the function prototype also return a pointer, like
char * getHeader( char* localString){....
Also, once you're done using the returned value, you need to free() it.
If header:data is the only use case you have you want to look for strchr().
Example:
#include <string.h> /* for strchr() and strlen() */
#include <errno.h> /* for EINVAL */
int get_header_and_data(const char * input, char ** pheader, char ** pdata)
{
int result = 0;
if (NULL == input
|| NULL == pheader || NULL == *pheader
|| NULL == pdata || NULL == *pdata
)
{
/* Trivial case of "no" input and/or missing references to store the result. */
errno = EINVAL;
result = -1;
}
else
{
char * pcolon = strchr(input, ':');
if (NULL == pcolon)
{
/* No delimiter found. */
errno = EINVAL;
result = -1;
}
else
{
/* Delimiter found. */
if (pcolon == input)
{
/* No header found. */
errno = EINVAL;
result = -1;
}
else
{
if (1 == strlen(pcolon))
{
/* No data found. */
errno = EINVAL;
result = -1;
}
else
{
/* Success. */
*pcolon = '\0';
++pcolon;
(*data) = pcolon;
(*pheader) = input;
}
}
}
}
return result;
}
and use it like this:
#include <stdio.h>
int get_header_and_data(const char *, char **, char **);
...
char content[50] = "";
/* Load content here. */
char * header = NULL;
char * data = NULL;
if (-1 == get_header_and_data(content, &header, &data)
{
perror("get_header_and_data() failed.");
abort(); /* Or what ever to handle the error. */
}
else
{
/* Dereference and/or use header and data here .*/
}
Please not that on success header and data (still) refer to the memory of content, as well as that on success the latter is modifed.
Just for fun, the code above can be shrunk down to:
int get_header_and_data(const char * input, char ** pheader, char ** pdata)
{
int result = 0;
if (NULL == input
|| NULL == pheader || NULL == *pheader
|| NULL == pdata || NULL == *pdata
)
{
/* Trivial case of "no" input and/or missing references to store the result. */
errno = EINVAL;
result = -1;
}
else
{
char * pcolon = strchr(input, ':');
if (NULL == pcolon /* No delimiter found. */
|| pcolon == input /* No header found. */
|| 1 == strlen(pcolon) /* No data found. */
)
{
errno = EINVAL;
result = -1;
}
else
{
/* Success. */
*pcolon = '\0';
++pcolon;
(*data) = pcolon;
(*pheader) = input;
}
}
return result;
}
Related
I need split data from ethernet. Data is in this format:
ZMXXX,angle*CHCK
Where angle is number. For example: ZMXXX,900*5A
And I need separated ZMXXX,900 and 5A. I wrote this function:
void split_data(char analyze[])
{
char *words[5]; uint8_t i=0;
words[i] = strtok(analyze,"*");
while(words[i]!=NULL)
{
words[++i] = strtok(NULL,"*");
}
}
And result is here:
And now, how I can get this data from variable:
words[0]
words[1]
Assuming the format you mention to be fixed, there is no need for the expensive and error-prone strtok().
Use the good old strchr():
int parse(char * input, char ** output)
{
int result = -1; /* Be pessimistic. */
if ((NULL == inout) || (NULL == output))
{
errno = EINVAL;
goto lblExit;
}
char * pc = strchr(analyze, '*');
if (NULL == pc);
{
errno = EINVAL;
goto lblExit;
}
*pc = '\0'; /* Set a temporary `0`-terminator. */
output[0] = strdup(analyze); /* Copy the 1st token. */
if (NULL == output[0])
{
goto lblExit;
}
*pc = '*'; /* Restore the original. */
output[1] = strdup(pc + 1); /* Seek beyond the `*` and copy the 2nd token. */
if (NULL == output[1])
{
free(outout[0]); /** Clean up. */
goto lblExit;
}
result = 0; /* Indicate success. */
lblExit:
return result;
}
Use it like this:
#define _POSIX_C_SOURCE 200809L /* To make strdup() available. */
#include <stdlib.h>
#include <stdio.h>
#include <errno.h>
int parse(char *, char **);
int main(void)
{
char data[] = "ZMXXX,900*5A";
char * words[2];
if (-1 == parse(data, words))
{
perror("parse() failed");
exit(EXIT_FAILURE);
}
printf("word 1 = '%s'\n", words[0]);
printf("word 2 = '%s'\n", words[1]);
free(words[0]);
free(words[1]);
return EXIT_SUCCESS;
}
The above code is expected to print:
word 1 = 'ZMXXX,900'
word 2 = '5A'
Note that strdup() isn't Standard C, but POSIX. It might need to be activated using one of the appropriate defines.
I've seen many solutions for getting substring of a string with usage of strndup or memcpy or strncpy and etc,.
I was wondering if there's a way to get substring without using those functions; even if it's unnecessary.
EDIT: I tried making function myself; I don't remember what the problem was but something went wrong and I ended up not using it.
char *substring(char *str, int start, int length) {
char *s = malloc(sizeof(char)*(length+1));
for(int i=start; i<start+length; i++) {
s[i-start] = str[i];
}
s[length] = '\0';
return s;
}
There are a number of ways to recreate strstr. The following is a quick implementation using the inch-worm method, where you simply use pointers to search for the beginning of the substring in string, then if found, compare every character in substring with the corresponding character in string. If all characters match, the substring is found, return a pointer to the beginning of substring in string.
If a character fails the test, look for another character in string that matches the first character in substring, until string is exhausted.
There are probably several more checks that can be inplemented, but this example should get you started:
#include <stdio.h>
#include <stdlib.h>
char *strstr2 (char *str, char *sub)
{
if (!str || !sub) return NULL; /* validate both strings */
char *p = NULL; /* general pointer */
char *sp = NULL; /* substring pointer */
char *rp = NULL; /* return pointer */
char matched = 0; /* matched flag */
size_t szstr = 0; /* string length */
size_t szsub = 0; /* substring length */
p = sub;
while (*p++) szsub++; /* strlen of substr */
p = str;
while (*p++) szstr++; /* strlen of str */
if (szsub > szstr) return NULL; /* szstr < szsub - no match */
p = str;
while (p < (p + szstr - szsub + 1))
{
while (*p && *p != *sub) p++; /* find start of sub in str */
if ((str + szstr) == p) return NULL; /* if end reached - no sub */
rp = p; /* save return pointer */
sp = sub; /* set sp to sub */
matched = 1; /* presume will match */
while (*sp) /* for each in substring */
if (*p++ != *sp++) { /* check if match fails */
matched = 0; /* if failed, no match */
break; /* break & find new start */
}
if (matched) /* if matched, return ptr */
return rp; /* to start of sub in str */
}
return NULL; /* no match, return NULL */
}
int main() {
char *string = NULL;
char *substr = NULL;
char *begin = NULL;
printf ("\nEnter string : ");
scanf ("%m[^\n]%*c", &string);
printf ("\nEnter substr : ");
scanf ("%m[^\n]%*c", &substr);
if ((begin = strstr2 (string, substr)) != NULL)
printf ("\nSubstring found beginning at : %s\n\n", begin);
else
printf ("\nSubstring NOT in string.\n\n");
if (string) free (string);
if (substr) free (substr);
return 0;
}
output:
$ ./bin/strstr
Enter string : This is the full string or "haystack".
Enter substr : g or "
Substring found beginning at : g or "haystack".
$ ./bin/strstr
Enter string : This is the full string or "haystack".
Enter substr : g or '
Substring NOT in string.
Wow!!! So many variables and tests and lots of indentation.
In the 1970's, some considered it poor style to not have all of the return
statements at the bottom of the routine, but that thinking has mostly disappeared.
For some reason, many programmers write their conditionals to test
if one variable is equal, not equal, greater, or less than something else.
They believe that conditionals should be boolean values and nothing else.
But C allows tests of int, char or others equal or not equal to zero.
Zero can be NULL or NUL or any other zero value. This is legal and appropriate.
if (variable) return NULL;
Some consider conditionals with side effects, such as,
if (*h++ == *n++) continue;
where variables h and n are modified, to not be great style.
To avoid that, I suppose you can rewrite it as
if (*h == *n) { h++; n++; continue;}
Here is my version. It is not worse than the version you supplied on this page. But I want to believe it is shorter, simpler, and easier to understand.
My style is not perfect. Nobody has perfect style. I supply this only
for contrast.
char * strstr( const char *haystack, const char *needle) {
const char *h = haystack, *n = needle;
for (;;) {
if (!*n) return (char *)h;
if (!*h) return NULL;
if (*n++ == *h++) continue;
h = ++haystack;
n = needle;
}
}
I have a string and an array of keywords. If the string contains one of the keywords on the list, I want to check if the keyword is the only element of this string. If it's not, I want to return an error. Last thing, the string will always end with \n.
My keyword array is the following:
const char * keywordsTable[] =
{
"INIT",
"BEGIN",
"END",
"ROUTINES",
"ENDROUTINES",
"ENDWHEN",
"WHEN",
"WHILE"
};
For instance if my string is "BEGIN\n", everything is fine. If my string is "BEGIN FOO\n" or "FOO BEGIN\n", I have to return an error. Finally if my string is "BEGINFOO\n", everything is fine. (error code is 1, else it's 0)
I've tried something (I don't know how to proceed):
int CheckKeyword(char * str)
{
int nKeywords = sizeof(keywordsTable) / sizeof(keywordsTable[0]);
char * strTok = NULL;
char * keywrdWithLF = malloc(20);
// I don't want to check for the last two keywords nor the first
for (int i = 1; i < nKeywords - 2; i++)
{
strcpy_s(keywrdWithLF, 20, keywordsTable[i]);
strcat_s(keywrdWithLF, 20, "\n");
strTok = strstr(str, keywrdWithLF);
// If my string contains a keyword
if (strTok != NULL)
{
// If the string contains other characters... and I'm stuck
if (strcmp(str, keywrdWithLF))
{
}
else
{
free(keywrdWithLF);
return 1;
}
}
}
free(keywrdWithLF);
return 0;
}
Thank you in advance (please don't complain bout my indent style, I have to use Whitesmith indent) !
int CheckKeyword(char * str)
{
int nKeywords = sizeof(keywordsTable) / sizeof(keywordsTable[0]);
char * strTok = NULL;
for (int i = 1; i < nKeywords - 2; i++)
{
if(NULL!=(strTok = strstr(str, keywordsTable[i])))
{
int len = strlen(keywordsTable[i]);
if(strTok == str)
{
if(str[len]==' ' || str[len]=='\t')
return 1;
}
else
{
if((strTok[-1]==' ' || strTok[-1]=='\t') && isspace(strTok[len]))//isspace in <ctype.h>
return 1;
}
}
}
return 0;
}
Perhaps another method?
int CheckKeyword(char * str)
{
int rCode=0;
int nKeywords = sizeof(keywordsTable) / sizeof(keywordsTable[0]);
char *keyword;
char *cp = keywordsTable;
I assume that since str is defined as "char * str" and not "const char * str", it is OK to modify the input string. Hence, why not just eliminate the '\n' problem from the equation?
/* Elininate the newline character from the end of the string. */
if((cp = strchr(str, '\n'))
*cp = \0;
// I don't want to check for the last two keywords nor the first.
nKeywords -= 3;
++keyword;
/* Loop through the keywords. */
while(nKeywords)
{
// "I want to check if the keyword is the only element of this string."
// "If it's not, I want to return an error."
if((cp=strstr(str, keyword))
{
/* Check for stuff prior to the keyword. */
if(cp != str)
rCode=1;
/* Check for stuff after the keyword. */
// Finally if my string is "BEGINFOO\n", everything is fine.
if(' ' == str[strlen[keyword])
rCode=1;
if(strcmp(cp, keyword))
rCode=1
break;
}
++keyword;
--nKeywords;
}
return(rCode);
}
I want to throw the last three character from file name and get the rest?
I have this code:
char* remove(char* mystr) {
char tmp[] = {0};
unsigned int x;
for (x = 0; x < (strlen(mystr) - 3); x++)
tmp[x] = mystr[x];
return tmp;
}
Try:
char *remove(char* myStr) {
char *retStr;
char *lastExt;
if (myStr == NULL) return NULL;
if ((retStr = malloc (strlen (myStr) + 1)) == NULL) return NULL;
strcpy (retStr, myStr);
lastExt = strrchr (retStr, '.');
if (lastExt != NULL)
*lastExt = '\0';
return retStr;
}
You'll have to free the returned string yourself. It simply finds the last . in the string and replaces it with a null terminator character. It will handle errors (passing NULL or running out of memory) by returning NULL.
It won't work with things like /this.path/is_bad since it will find the . in the non-file portion but you could handle this by also doing a strrchr of /, or whatever your path separator is, and ensuring it's position is NULL or before the . position.
A more general purpose solution to this problem could be:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
// remove_ext: removes the "extension" from a file spec.
// myStr is the string to process.
// extSep is the extension separator.
// pathSep is the path separator (0 means to ignore).
// Returns an allocated string identical to the original but
// with the extension removed. It must be freed when you're
// finished with it.
// If you pass in NULL or the new string can't be allocated,
// it returns NULL.
char *remove_ext (char* myStr, char extSep, char pathSep) {
char *retStr, *lastExt, *lastPath;
// Error checks and allocate string.
if (myStr == NULL) return NULL;
if ((retStr = malloc (strlen (myStr) + 1)) == NULL) return NULL;
// Make a copy and find the relevant characters.
strcpy (retStr, myStr);
lastExt = strrchr (retStr, extSep);
lastPath = (pathSep == 0) ? NULL : strrchr (retStr, pathSep);
// If it has an extension separator.
if (lastExt != NULL) {
// and it's to the right of the path separator.
if (lastPath != NULL) {
if (lastPath < lastExt) {
// then remove it.
*lastExt = '\0';
}
} else {
// Has extension separator with no path separator.
*lastExt = '\0';
}
}
// Return the modified string.
return retStr;
}
int main (int c, char *v[]) {
char *s;
printf ("[%s]\n", (s = remove_ext ("hello", '.', '/'))); free (s);
printf ("[%s]\n", (s = remove_ext ("hello.", '.', '/'))); free (s);
printf ("[%s]\n", (s = remove_ext ("hello.txt", '.', '/'))); free (s);
printf ("[%s]\n", (s = remove_ext ("hello.txt.txt", '.', '/'))); free (s);
printf ("[%s]\n", (s = remove_ext ("/no.dot/in_path", '.', '/'))); free (s);
printf ("[%s]\n", (s = remove_ext ("/has.dot/in.path", '.', '/'))); free (s);
printf ("[%s]\n", (s = remove_ext ("/no.dot/in_path", '.', 0))); free (s);
return 0;
}
and this produces:
[hello]
[hello]
[hello]
[hello.txt]
[/no.dot/in_path]
[/has.dot/in]
[/no]
Use rindex to locate the "." character. If the string is writable, you can replace it with the string terminator char ('\0') and you're done.
char * rindex(const char *s, int c);
DESCRIPTION
The rindex() function locates the last character matching c (converted to a char) in the null-terminated string s.
If you literally just want to remove the last three characters, because you somehow know that your filename has an extension exactly three chars long (and you want to keep the dot):
char *remove_three(const char *filename) {
size_t len = strlen(filename);
char *newfilename = malloc(len-2);
if (!newfilename) /* handle error */;
memcpy(newfilename, filename, len-3);
newfilename[len - 3] = 0;
return newfilename;
}
Or let the caller provide the destination buffer (which they must ensure is long enough):
char *remove_three(char *dst, const char *filename) {
size_t len = strlen(filename);
memcpy(dst, filename, len-3);
dst[len - 3] = 0;
return dst;
}
If you want to generically remove a file extension, that's harder, and should normally use whatever filename-handling routines your platform provides (basename on POSIX, _wsplitpath_s on Windows) if there's any chance that you're dealing with a path rather than just the final part of the filename:
/* warning: may modify filename. To avoid this, take a copy first
dst may need to be longer than filename, for example currently
"file.txt" -> "./file.txt". For this reason it would be safer to
pass in a length with dst, and/or allow dst to be NULL in which
case return the length required */
void remove_extn(char *dst, char *filename) {
strcpy(dst, dirname(filename));
size_t len = strlen(dst);
dst[len] = '/';
dst += len+1;
strcpy(dst, basename(filename));
char *dot = strrchr(dst, '.');
/* retain the '.' To remove it do dot[0] = 0 */
if (dot) dot[1] = 0;
}
Come to think of it, you might want to pass dst+1 rather than dst to strrchr, since a filename starting with a dot maybe shouldn't be truncated to just ".". Depends what it's for.
I would try the following algorithm:
last_dot = -1
for each char in str:
if char = '.':
last_dot = index(char)
if last_dot != -1:
str[last_dot] = '\0'
Just replace the dot with "0". If you know that your extension is always 3 characters long you can just do:
char file[] = "test.png";
file[strlen(file) - 4] = 0;
puts(file);
This will output "test". Also, you shouldn't return a pointer to a local variable. The compiler will also warn you about this.
To get paxdiablo's second more general purpose solution to work in a C++ compiler I changed this line:
if ((retstr = malloc (strlen (mystr) + 1)) == NULL)
to:
if ((retstr = static_cast<char*>(malloc (strlen (mystr) + 1))) == NULL)
Hope this helps someone.
This should do the job:
char* remove(char* oldstr) {
int oldlen = 0;
while(oldstr[oldlen] != NULL){
++oldlen;
}
int newlen = oldlen - 1;
while(newlen > 0 && mystr[newlen] != '.'){
--newlen;
}
if (newlen == 0) {
newlen = oldlen;
}
char* newstr = new char[newlen];
for (int i = 0; i < newlen; ++i){
newstr[i] = oldstr[i];
}
return newstr;
}
Get location and just copy up to that location into a new char *.
i = 0;
n = 0;
while(argv[1][i] != '\0') { // get length of filename
i++; }
for(ii = 0; i > -1; i--) { // look for extension working backwards
if(argv[1][i] == '.') {
n = i; // char # of exension
break; } }
memcpy(new_filename, argv[1], n);
This is simple way to change extension name.
....
char outputname[255]
sscanf(inputname,"%[^.]",outputname); // foo.bar => foo
sprintf(outputname,"%s.txt",outputname) // foo.txt <= foo
....
With configurable minimum file length and configurable maximum extension length. Returns index where extension was changed to null character, or -1 if no extension was found.
int32_t strip_extension(char *in_str)
{
static const uint8_t name_min_len = 1;
static const uint8_t max_ext_len = 4;
/* Check chars starting at end of string to find last '.' */
for (ssize_t i = sizeof(in_str); i > (name_min_len + max_ext_len); i--)
{
if (in_str[i] == '.')
{
in_str[i] = '\0';
return i;
}
}
return -1;
}
I use this code:
void remove_extension(char* s) {
char* dot = 0;
while (*s) {
if (*s == '.') dot = s; // last dot
else if (*s == '/' || *s == '\\') dot = 0; // ignore dots before path separators
s++;
}
if (dot) *dot = '\0';
}
It handles the Windows path convention correctly (both / and \ can be path separators).
Supposing I have this:
"foo bar 1 and foo bar 2"
How can I split it into:
foo bar 1
foo bar 2
?
I tried strtok() and strsep() but neither worked. They don't recognize "and" as delimiter, they recognize "a", "n" and "d" as delimiters.
Any function to help me with this or I'll have to split by the blank space and do some string manipulation?
You can use strstr() to find the first " and ", and "tokenize" the string yourself by just skipping forward so many characters, and doing it again.
The primary problem with splitting strings in C is that it inevitably
results in some dynamic memory management, and that tends to be avoided
by the standard library whenever possible. That is why none of the standard
C functions deal with dynamic memory allocation, only malloc/calloc/realloc
do that.
But it is not too difficult to do this oneself. Let me walk you through
it.
We need to return a number of strings, and the easiest way to do that
is to return an array of pointers to strings, which is terminated by
a NULL item. Except for the final NULL, each element in the array points to
a dynamically allocated string.
First we need a couple of helper functions to deal with such arrays.
The easiest one is one that computes the number of strings (elements
before the final NULL):
/* Return length of a NULL-delimited array of strings. */
size_t str_array_len(char **array)
{
size_t len;
for (len = 0; array[len] != NULL; ++len)
continue;
return len;
}
Another easy one is the function for freeing the array:
/* Free a dynamic array of dynamic strings. */
void str_array_free(char **array)
{
if (array == NULL)
return;
for (size_t i = 0; array[i] != NULL; ++i)
free(array[i]);
free(array);
}
Somewhat more complicated is the function that adds a copy of a string
to the array. It needs to handle a number of special cases, such as when
the array does not exist yet (the whole array is NULL). Also, it needs to
handle strings that are not terminated with '\0' so that it is easier for
our actual splitting function to just use parts of the input string when
appending.
/* Append an item to a dynamically allocated array of strings. On failure,
return NULL, in which case the original array is intact. The item
string is dynamically copied. If the array is NULL, allocate a new
array. Otherwise, extend the array. Make sure the array is always
NULL-terminated. Input string might not be '\0'-terminated. */
char **str_array_append(char **array, size_t nitems, const char *item,
size_t itemlen)
{
/* Make a dynamic copy of the item. */
char *copy;
if (item == NULL)
copy = NULL;
else {
copy = malloc(itemlen + 1);
if (copy == NULL)
return NULL;
memcpy(copy, item, itemlen);
copy[itemlen] = '\0';
}
/* Extend array with one element. Except extend it by two elements,
in case it did not yet exist. This might mean it is a teeny bit
too big, but we don't care. */
array = realloc(array, (nitems + 2) * sizeof(array[0]));
if (array == NULL) {
free(copy);
return NULL;
}
/* Add copy of item to array, and return it. */
array[nitems] = copy;
array[nitems+1] = NULL;
return array;
}
That's a moutful. For really good style, it would have been better to
split off the making of a dynamic copy if the input item to its own
function, but I will leave that as an excercise to the reader.
Finally, we have the actual splitting function. It, also, needs to handle
some special cases:
The input string might start or end with the separator.
There might be separators right next to each other.
The input string might not include the separator at all.
I have opted to add an empty string to the result if the separator is
right next to the start or end of the input string, or right next to
another separator. If you need something else, you need to tweak the
code.
Other than the special cases, and some error handling, the splitting
is now reasonably straightforward.
/* Split a string into substrings. Return dynamic array of dynamically
allocated substrings, or NULL if there was an error. Caller is
expected to free the memory, for example with str_array_free. */
char **str_split(const char *input, const char *sep)
{
size_t nitems = 0;
char **array = NULL;
const char *start = input;
char *next = strstr(start, sep);
size_t seplen = strlen(sep);
const char *item;
size_t itemlen;
for (;;) {
next = strstr(start, sep);
if (next == NULL) {
/* Add the remaining string (or empty string, if input ends with
separator. */
char **new = str_array_append(array, nitems, start, strlen(start));
if (new == NULL) {
str_array_free(array);
return NULL;
}
array = new;
++nitems;
break;
} else if (next == input) {
/* Input starts with separator. */
item = "";
itemlen = 0;
} else {
item = start;
itemlen = next - item;
}
char **new = str_array_append(array, nitems, item, itemlen);
if (new == NULL) {
str_array_free(array);
return NULL;
}
array = new;
++nitems;
start = next + seplen;
}
if (nitems == 0) {
/* Input does not contain separator at all. */
assert(array == NULL);
array = str_array_append(array, nitems, input, strlen(input));
}
return array;
}
Here is the whole program in one piece. It also includes a main program
to run some test cases.
#include <assert.h>
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
/* Append an item to a dynamically allocated array of strings. On failure,
return NULL, in which case the original array is intact. The item
string is dynamically copied. If the array is NULL, allocate a new
array. Otherwise, extend the array. Make sure the array is always
NULL-terminated. Input string might not be '\0'-terminated. */
char **str_array_append(char **array, size_t nitems, const char *item,
size_t itemlen)
{
/* Make a dynamic copy of the item. */
char *copy;
if (item == NULL)
copy = NULL;
else {
copy = malloc(itemlen + 1);
if (copy == NULL)
return NULL;
memcpy(copy, item, itemlen);
copy[itemlen] = '\0';
}
/* Extend array with one element. Except extend it by two elements,
in case it did not yet exist. This might mean it is a teeny bit
too big, but we don't care. */
array = realloc(array, (nitems + 2) * sizeof(array[0]));
if (array == NULL) {
free(copy);
return NULL;
}
/* Add copy of item to array, and return it. */
array[nitems] = copy;
array[nitems+1] = NULL;
return array;
}
/* Free a dynamic array of dynamic strings. */
void str_array_free(char **array)
{
if (array == NULL)
return;
for (size_t i = 0; array[i] != NULL; ++i)
free(array[i]);
free(array);
}
/* Split a string into substrings. Return dynamic array of dynamically
allocated substrings, or NULL if there was an error. Caller is
expected to free the memory, for example with str_array_free. */
char **str_split(const char *input, const char *sep)
{
size_t nitems = 0;
char **array = NULL;
const char *start = input;
char *next = strstr(start, sep);
size_t seplen = strlen(sep);
const char *item;
size_t itemlen;
for (;;) {
next = strstr(start, sep);
if (next == NULL) {
/* Add the remaining string (or empty string, if input ends with
separator. */
char **new = str_array_append(array, nitems, start, strlen(start));
if (new == NULL) {
str_array_free(array);
return NULL;
}
array = new;
++nitems;
break;
} else if (next == input) {
/* Input starts with separator. */
item = "";
itemlen = 0;
} else {
item = start;
itemlen = next - item;
}
char **new = str_array_append(array, nitems, item, itemlen);
if (new == NULL) {
str_array_free(array);
return NULL;
}
array = new;
++nitems;
start = next + seplen;
}
if (nitems == 0) {
/* Input does not contain separator at all. */
assert(array == NULL);
array = str_array_append(array, nitems, input, strlen(input));
}
return array;
}
/* Return length of a NULL-delimited array of strings. */
size_t str_array_len(char **array)
{
size_t len;
for (len = 0; array[len] != NULL; ++len)
continue;
return len;
}
#define MAX_OUTPUT 20
int main(void)
{
struct {
const char *input;
const char *sep;
char *output[MAX_OUTPUT];
} tab[] = {
/* Input is empty string. Output should be a list with an empty
string. */
{
"",
"and",
{
"",
NULL,
},
},
/* Input is exactly the separator. Output should be two empty
strings. */
{
"and",
"and",
{
"",
"",
NULL,
},
},
/* Input is non-empty, but does not have separator. Output should
be the same string. */
{
"foo",
"and",
{
"foo",
NULL,
},
},
/* Input is non-empty, and does have separator. */
{
"foo bar 1 and foo bar 2",
" and ",
{
"foo bar 1",
"foo bar 2",
NULL,
},
},
};
const int tab_len = sizeof(tab) / sizeof(tab[0]);
bool errors;
errors = false;
for (int i = 0; i < tab_len; ++i) {
printf("test %d\n", i);
char **output = str_split(tab[i].input, tab[i].sep);
if (output == NULL) {
fprintf(stderr, "output is NULL\n");
errors = true;
break;
}
size_t num_output = str_array_len(output);
printf("num_output %lu\n", (unsigned long) num_output);
size_t num_correct = str_array_len(tab[i].output);
if (num_output != num_correct) {
fprintf(stderr, "wrong number of outputs (%lu, not %lu)\n",
(unsigned long) num_output, (unsigned long) num_correct);
errors = true;
} else {
for (size_t j = 0; j < num_output; ++j) {
if (strcmp(tab[i].output[j], output[j]) != 0) {
fprintf(stderr, "output[%lu] is '%s' not '%s'\n",
(unsigned long) j, output[j], tab[i].output[j]);
errors = true;
break;
}
}
}
str_array_free(output);
printf("\n");
}
if (errors)
return EXIT_FAILURE;
return 0;
}
Here's a nice short example I just wrote up showing how to use strstr to split a string on a given string:
#include <string.h>
#include <stdio.h>
void split(char *phrase, char *delimiter)
{
char *loc = strstr(phrase, delimiter);
if (loc == NULL)
{
printf("Could not find delimiter\n");
}
else
{
char buf[256]; /* malloc would be much more robust here */
int length = strlen(delimiter);
strncpy(buf, phrase, loc - phrase);
printf("Before delimiter: '%s'\n", buf);
printf("After delimiter: '%s'\n", loc+length);
}
}
int main()
{
split("foo bar 1 and foo bar 2", "and");
printf("-----\n");
split("foo bar 1 and foo bar 2", "quux");
return 0;
}
Output:
Before delimiter: 'foo bar 1 '
After delimiter: ' foo bar 2'
-----
Could not find delimiter
Of course, I haven't fully tested it, and it's probably vulnerable to most of the standard buffer overflow issues related to string lengths; but it's a demonstrable example at the very least.
If you know the type of delimiter example comma or semicolon you can try with this:
#include<stdio.h>
#include<conio.h>
int main()
{
int i=0,temp=0,temp1=0, temp2=0;
char buff[12]="123;456;789";
for(i=0;buff[i]!=';',i++)
{
temp=temp*10+(buff[i]-48);
}
for(i=0;buff[i]!=';',i++)
{
temp1=temp1*10+(buff[i]-48);
}
for(i=0;buff[i],i++)
{
temp2=temp2*10+(buff[i]-48);
}
printf("temp=%d temp1=%d temp2=%d",temp,temp1,temp2);
getch();
return 0;
}
Output:
temp=123 temp1=456 temp2=789