Strchr and strrchr returning same result - c

For some reason, whether I try strchr or strrchr, I get the same value returned. I have no idea why.
Here is the segment of code giving me trouble:
printf("Enter a data point (-1 to stop input):\n");
fgets(input, 50, stdin);
char *name = strrchr(input, ',');
if(name)
{
printf("%s", name);
}
The input is Jane Austen, 6, and I am trying to separate it into two strings: one before the comma and one after the comma. However, my use of strrchr(input, ','); or strchr(input, ','); seems pointless, as my output is ALWAYS , 6. Can someone explain why?

It sounds like you want strtok instead:
char *name = strtok(input, ",");
char *value = strtok(NULL, ",");

Some languages provide a string function split that takes a string or regular expression and splits the string into a list of substrings separated by the delimiter (python, ruby, perl). It is not too difficult to construct such a split function, especially if you just split on a single character.
char** split(char* string, char delim);
You will also want a string join function, and a function to cleanup the allocated space.
char* split_join(char** splitray, char* buffer);
void split_free(char** splitray);

Related

Weird output from strtok

I was having some issues dealing with char*'s from an array of char*'s and used this for reference: Splitting C char array into words
So what I'm trying to do is read in char arrays and split them with a space delimiter so I can do stuff with it. For example if the first token in my char* is "Dog" I would send it to a different function that dealt with dogs. My problem is that I'm getting a strange output.
For example:
INPUT: *cmd = "Dog needs a vet appointment."
OUTPUT: (from print statements) "Doneeds a vet appntment."
I've checked for memory leaks using valgrind and I have none of them or other errors.
void parseCmd(char* cmd){ //passing in an individual char* from a char**
char** p_args = calloc(100, sizeof(char*));
int i = 0;
char* token;
token = strtok(cmd, " ");
while (token != NULL){
p_args[i++] = token;
printf("%s",token); //trying to debug
token = strtok(NULL, cmd);
}
free(p_args);
}
Any advice? I am new to C so please bear with me if I did something stupid. Thank you.
In your case,
token = strtok(NULL, cmd);
is not what you should be doing. You instead need:
token = strtok(NULL, " ");
As per the ISO standard:
char *strtok(char * restrict s1, const char * restrict s2);
A sequence of calls to the strtok function breaks the string pointed to by s1 into a sequence of tokens, each of which is delimited by a character from the string pointed to by s2.
The only difference between the first and subsequent calls (assuming, as per this case, you want the same delimiters) should be using NULL as the input string rather than the actual string. By using the input string as the delimiter list in subsequent calls, you change the behaviour.
You can see exactly what's happening if you try the following code:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
void parseCmd(char* cmd) {
char* token = strtok(cmd, " ");
while (token != NULL) {
printf("[%s] [%s]\n", cmd, token);
token = strtok(NULL, cmd);
}
}
int main(void) {
char x[] = "Dog needs a vet appointment.";
parseCmd(x);
return 0;
}
which outputs (first column will be search string to use next iteration, second is result of this iteration):
[Dog] [Dog]
[Dog] [needs a vet app]
[Dog] [intment.]
The first step worked fine since you were using space as the delimiter and it modified the string by placing a \0 at the end of Dog.
That means the next attempt (with the wrong spearator) would use one of the letters from {D,o,g} to split. The first matching letter for that set is the o in appointment which is why you see needs a vet app. The third attempt finds none of the candidate letters so you just get back the rest of the string, intment..
token = strtok(NULL, cmd); should be token = strtok(NULL, " ");.
The second argument is for delimiter.
http://man7.org/linux/man-pages/man3/strtok.3.html

Split a string with delimiters with support for missing values C99 [duplicate]

I am trying to tokenize a string but I need to know exactly when no data is seen between two tokens. e.g when tokenizing the following string "a,b,c,,,d,e" I need to know about the two empty slots between 'd' and 'e'... which I am unable to find out simply using strtok(). My attempt is shown below:
char arr_fields[num_of_fields];
char delim[]=",\n";
char *tok;
tok=strtok(line,delim);//line contains the data
for(i=0;i<num_of_fields;i++,tok=strtok(NULL,delim))
{
if(tok)
sprintf(arr_fields[i], "%s", tok);
else
sprintf(arr_fields[i], "%s", "-");
}
Executing the above code with the aforementioned examples put characters a,b,c,d,e into first five elements of arr_fields which is not desirable. I need the position of each character to go in specific indexes of array: i.e if there is a character missing between two characters, it should be recorded as is.
7.21.5.8 the strtok function
The standard says the following regarding strtok:
[#3] The first call in the sequence searches the string
pointed to by s1 for the first character that is not
contained in the current separator string pointed to by s2.
If no such character is found, then there are no tokens in
the string pointed to by s1 and the strtok function returns
a null pointer. If such a character is found, it is the
start of the first token.
In the above quote we can read you cannot use strtok as a solution to your specific problem, since it will treat any sequential characters found in delims as a single token.
Am I doomed to weep in silence, or can somebody help me out?
You can easily implement your own version of strtok that does what you want, see the snippets at the end of this post.
strtok_single makes use of strpbrk (char const* src, const char* delims) which will return a pointer to the first occurrence of any character in delims that is found in the null-terminated string src.
If no matching character is found the function will return NULL.
strtok_single
char *
strtok_single (char * str, char const * delims)
{
static char * src = NULL;
char * p, * ret = 0;
if (str != NULL)
src = str;
if (src == NULL)
return NULL;
if ((p = strpbrk (src, delims)) != NULL) {
*p = 0;
ret = src;
src = ++p;
} else if (*src) {
ret = src;
src = NULL;
}
return ret;
}
sample use
char delims[] = ",";
char data [] = "foo,bar,,baz,biz";
char * p = strtok_single (data, delims);
while (p) {
printf ("%s\n", *p ? p : "<empty>");
p = strtok_single (NULL, delims);
}
output
foo
bar
<empty>
baz
biz
You can't use strtok() if that's what you want. From the man page:
A sequence of two or more contiguous delimiter characters in the parsed
string is considered to be a single delimiter. Delimiter characters at
the start or end of the string are ignored. Put another way: the
tokens returned by strtok() are always nonempty strings.
Therefore it is just going to jump from c to d in your example.
You're going to have to parse the string manually or perhaps search for a CSV parsing library that would make your life easier.
Lately I was looking for a solution to the same problem and found this thread.
You can use strsep().
From the manual:
The strsep() function was introduced as a replacement for strtok(3),
since the latter cannot handle empty fields.
As mentioned in this answer, you'll want to implement something like strtok yourself. I prefer using strcspn (as opposed to strpbrk), as it allows for fewer if statements:
char arr_fields[num_of_fields];
char delim[]=",\n";
char *tok;
int current_token= 0;
int token_length;
for (i = 0; i < num_of_fields; i++, token_length = strcspn(line + current_token,delim))
{
if(token_length)
sprintf(arr_fields[i], "%.*s", token_length, line + current_token);
else
sprintf(arr_fields[i], "%s", "-");
current_token += token_length;
}
Parse (for example, strtok)
Sort
Insert
Rinse and repeat as needed :)
You could try using strchr to find out the locations of the , symbols. Tokenize manually your string up to the token you found (using memcpy or strncpy) and then use again strchr. You will be able to see if two or more commas are next to each other this way (strchr will return numbers that their subtraction will equal 1) and you can write an if statement to handle that case.

Splitting string around a character in C

I have a character like ';' or ',' used as a delimeter in a raw string. I need to split the string and iterate over each string.
Ex: If,
char* str = "apples, mangoes , orang; ,ad";
And the delimiter is ',' then I need something like
while(substr!='\0') {
func(substr);
//some operation maybe like substr=strstr(substr)+1;
}
The function should be called 4 times with strings: "apples"," mangoes "," orang; ","ad".
In your case str is string literal and you cannot use strtok on that since its in R_ONLY section.
and strtok does modify your str which is literal and would yield runtime error (Segmentation Fault).
If you want to split it then you must have an user input. Probably fgets (most preferred one).
fgets (str, SIZE, stdin); // user input for str
Make use of strtok function only if you have user input for a string.
char * strtok (char *string, const char *delimiter);
This is how you can use it.
char *buff = strtok (str, ",;");
while (buff != NULL)
{
printf (buff);
buff = strtok (NULL, ",;");
}
For more information on the string functions
man string
strtok is handy tool for tokenize the string in C. Also note strtok modify the original string.
In your case
char* str = "apples, mangoes , orang; ,ad";
This is string literal which is read only and it's Undefined Behaviour if you use strtok on this. So batter to use predefined length or array or copy this string to some temp buffer then apply strtok on temp buffer.
For example
int main()
{
char str[] = "apples, mangoes , orang; ,ad";
char *token = strtok (str, ",;");
while (token != NULL)
{
printf ("%s ",token);
token = strtok (NULL, ",;");
}
return 0;
}

Need to know when no data appears between two token separators using strtok()

I am trying to tokenize a string but I need to know exactly when no data is seen between two tokens. e.g when tokenizing the following string "a,b,c,,,d,e" I need to know about the two empty slots between 'd' and 'e'... which I am unable to find out simply using strtok(). My attempt is shown below:
char arr_fields[num_of_fields];
char delim[]=",\n";
char *tok;
tok=strtok(line,delim);//line contains the data
for(i=0;i<num_of_fields;i++,tok=strtok(NULL,delim))
{
if(tok)
sprintf(arr_fields[i], "%s", tok);
else
sprintf(arr_fields[i], "%s", "-");
}
Executing the above code with the aforementioned examples put characters a,b,c,d,e into first five elements of arr_fields which is not desirable. I need the position of each character to go in specific indexes of array: i.e if there is a character missing between two characters, it should be recorded as is.
7.21.5.8 the strtok function
The standard says the following regarding strtok:
[#3] The first call in the sequence searches the string
pointed to by s1 for the first character that is not
contained in the current separator string pointed to by s2.
If no such character is found, then there are no tokens in
the string pointed to by s1 and the strtok function returns
a null pointer. If such a character is found, it is the
start of the first token.
In the above quote we can read you cannot use strtok as a solution to your specific problem, since it will treat any sequential characters found in delims as a single token.
Am I doomed to weep in silence, or can somebody help me out?
You can easily implement your own version of strtok that does what you want, see the snippets at the end of this post.
strtok_single makes use of strpbrk (char const* src, const char* delims) which will return a pointer to the first occurrence of any character in delims that is found in the null-terminated string src.
If no matching character is found the function will return NULL.
strtok_single
char *
strtok_single (char * str, char const * delims)
{
static char * src = NULL;
char * p, * ret = 0;
if (str != NULL)
src = str;
if (src == NULL)
return NULL;
if ((p = strpbrk (src, delims)) != NULL) {
*p = 0;
ret = src;
src = ++p;
} else if (*src) {
ret = src;
src = NULL;
}
return ret;
}
sample use
char delims[] = ",";
char data [] = "foo,bar,,baz,biz";
char * p = strtok_single (data, delims);
while (p) {
printf ("%s\n", *p ? p : "<empty>");
p = strtok_single (NULL, delims);
}
output
foo
bar
<empty>
baz
biz
You can't use strtok() if that's what you want. From the man page:
A sequence of two or more contiguous delimiter characters in the parsed
string is considered to be a single delimiter. Delimiter characters at
the start or end of the string are ignored. Put another way: the
tokens returned by strtok() are always nonempty strings.
Therefore it is just going to jump from c to d in your example.
You're going to have to parse the string manually or perhaps search for a CSV parsing library that would make your life easier.
Lately I was looking for a solution to the same problem and found this thread.
You can use strsep().
From the manual:
The strsep() function was introduced as a replacement for strtok(3),
since the latter cannot handle empty fields.
As mentioned in this answer, you'll want to implement something like strtok yourself. I prefer using strcspn (as opposed to strpbrk), as it allows for fewer if statements:
char arr_fields[num_of_fields];
char delim[]=",\n";
char *tok;
int current_token= 0;
int token_length;
for (i = 0; i < num_of_fields; i++, token_length = strcspn(line + current_token,delim))
{
if(token_length)
sprintf(arr_fields[i], "%.*s", token_length, line + current_token);
else
sprintf(arr_fields[i], "%s", "-");
current_token += token_length;
}
Parse (for example, strtok)
Sort
Insert
Rinse and repeat as needed :)
You could try using strchr to find out the locations of the , symbols. Tokenize manually your string up to the token you found (using memcpy or strncpy) and then use again strchr. You will be able to see if two or more commas are next to each other this way (strchr will return numbers that their subtraction will equal 1) and you can write an if statement to handle that case.

What is a good way of looping through lines in a multi-line string?

My function foo(char *str) receives str that is a multiline string with new line characters that is null-terminated. I am trying to write a while loop that iterates through the string and operates on one line. What is a good way of achieving this?
void foo(char *str) {
while((line=getLine(str)) != NULL) {
// Process a line
}
}
Do I need to implement getLine myself or is there an in-built function to do this for me?
You will need to implement some kind of parsing based on the new line character yourself. strtok() with a delimiter of "\n" is a pretty good option that does something like what you're looking for but it has to be used slightly differently than your example. It would be more like:
char *tok;
char *delims = "\n";
tok = strtok(str, delims);
while (tok != NULL) {
// process the line
//advance the token
tok = strtok(NULL, delims);
}
You should note, however, that strtok() is both destructive and not threadsafe.
I think you might use strtok, which tokenizes a string into packets delimited by some specific characters, in your case the newline character:
void foo(char *str)
{
char *line = strtok(str, "\n");
while(line)
{
//work with line, which contains a single line without the trailing '\n'
...
//next line
line = strtok(NULL, "\n");
}
}
But keep in mind that this alters the contents of str (it actually replaces the '\n's by '\0's), so you may want to make a copy of it beforehand if you need it further.
Sooo... a bit late, but below is a re-entrant version of #debeer's and #Christian Rau's answer - notice strtok_r instead of strtok.
This can be called from multiple threads using different strings.
char *tok;
char *saveptr;
char *delims = "\n";
tok = strtok_r(str, delims, &saveptr);
while (tok != NULL) {
// process the line
//advance the token
tok = strtok_r(NULL, delims, &saveptr);
}
Please note that it is still destructive as it modifies the string being tokenised.
You can use fgets to do the getLine work: http://linux.die.net/man/3/fgets

Resources