2

I have a gps string like below:

char gps_string[] = "$GPRMC,080117.000,A,4055.1708,N,02918.9336,E,0.00,316.26,00,,,A*78";

I want to parse the substrings between the commas like below sequence:

$GPRMC
080117.000
A
4055.1708
.
.
.

I have tried sscanf function like below:

 sscanf(gps_string,"%s,%s,%s,%s,%s,",char1,char2,char3,char4,char5);

But this is not working. char1 array gets the whole string if use above function.

Actually i have used strchr function in my previous algorithm and got it work but it's easier and simplier if i can get it work with sscanf and get those parameters in substring.

By the way, substrings between the commas can vary. But the comma sequence is fixed. For example below is another gps string example but it does not contain some of its parts because of sattellite problem:

char gps_string[] = "$GPRMC,001041.799,V,,,,,0.00,0.00,060180,,,N*"
abdullah cinar
  • 543
  • 1
  • 6
  • 20
  • 1
    use `strtok()` to split the string – milevyo Feb 05 '16 at 15:57
  • 1
    The above two comments are good solutions but `strstr` is not. – Carey Gregory Feb 05 '16 at 15:59
  • @user3121023 Wow. That worked like a magic! What is those syntax %[',] – abdullah cinar Feb 05 '16 at 15:59
  • 1
    Possible duplicate of [Split string with delimiters in C](http://stackoverflow.com/questions/9210528/split-string-with-delimiters-in-c) – SegFault Feb 05 '16 at 16:00
  • Yes, i think i can use strtok too. Thanks for the suggestion. I will try it also and chose the best to use. – abdullah cinar Feb 05 '16 at 16:00
  • 1
    @abdullahcinar: You'll want to read the documentation on the scanf functions. But `[^,]` matches every character that is **not** a comma. – Zan Lynx Feb 05 '16 at 16:01
  • Thank you all for the answers, worked a lot. @PhotometricStereo Sorry i guess i couldn't find the true keywords to find that similar question. Should i delete this question? – abdullah cinar Feb 05 '16 at 16:02
  • Sorry for the inconvenience, i just got realized that i had a mistyping at the "strstr" in the question. I have nothing to do with it. I was meaning "sscanf" function. But still you gave the true solutions, thanks. – abdullah cinar Feb 05 '16 at 16:09
  • 1
    An alternative is to find the next ',' using `strpbrk()`. If you do this, you would write the loop to get the substring and update the pointer to find the next comma. Some comments on other threads mention this to avoid some of the problems with `strtok()` – sabbahillel Feb 05 '16 at 16:12
  • Note even if a question is a duplicate, it is often a good idea not to delete it. That way someone could find the question (and answer) with a search for different key words. – sabbahillel Feb 05 '16 at 16:14
  • @sabbahillel That's a good idea :) Thanks for the both advices. I will have a look at that function too. – abdullah cinar Feb 05 '16 at 16:15
  • http://stackoverflow.com/questions/7100214/strtok-segfault says "Though at a higher level, you really should think twice before using strtok. It is deprecated, non-reentrant and widely consider to be one of the worst designed function in the C standard library". – sabbahillel Feb 05 '16 at 16:46
  • I put in an answer using `strpbrk()` – sabbahillel Feb 05 '16 at 17:17
  • @chux Note that the statement "it is deprecated" is a quote from the answer linked to. He appears to be using the term instead of the term "discouraged" as it is still a valid function that can be called. The comment above just used the direct quote instead of paraphrasing it. – sabbahillel Feb 05 '16 at 19:31
  • @sabbahillel Thanks - moved that comment to the source. – chux - Reinstate Monica Feb 05 '16 at 19:34

2 Answers2

3

You can use strtok:

#include <stdio.h>

int main(void) {
    char gps_string[] = "$GPRMC,080117.000,A,4055.1708,N,02918.9336,E,0.00,316.26,00,,,A*78";
    char* c = strtok(gps_string, ",");
    while (c != NULL) {
        printf("%s\n", c);
        c = strtok(NULL, ",");
    }
    return 0;
}

EDIT: As Carey Gregory mentioned, strtok modifies the given string. This is explained in the man page I linked to, and you can find some details here too.

Community
  • 1
  • 1
  • Thank you for the answer, this worked very well. What about using sscanf? What would be the differences between using strtok as you mentioned, or using sscanf as the corrected version of mine from user3121023's answer? – abdullah cinar Feb 05 '16 at 16:10
  • I guess user3121023 has deleted his answer :/ Here is his suggestion, this works too: `sscanf(gps_string,"%[',],%[',],%[',],%[',],%[',]",char1,char2,char3,char4,char5); ` – abdullah cinar Feb 05 '16 at 16:31
  • 1
    manual says "A sequence of two or more contiguous delimiter characters in the parsed string is considered to be a single delimiter" OP said that it is possible to have that occur in the example at the end.. – sabbahillel Feb 05 '16 at 16:48
  • 1
    @abdullah cinar ``"%[',],%[',],%[',],%[',],%[',]"`` does not work. Did you mean ``"%[^,],%[^,],%[^,],%[^,],%[^,]"``? That also has trouble with consecutive `,,` and so is not a good solution. – chux - Reinstate Monica Feb 05 '16 at 19:15
  • Yes, sorry i had meant that. But i didn't try it with consecutive `,,` So, i can look for the other options then. – abdullah cinar Feb 06 '16 at 17:43
3

There have been a number of comments in other answers stating that there are a number of problems with strtok() and suggesting using strpbrk() instead. An example of how this is used can be found at Arrays and strpbrk in C

I do not have a compiler available so I could not test this. I could have typos or other misteaks in the code, but I am sure that you can figure out what is meant.

In this case you would use

char *String_Buffer = gps_string;
char *start = String_Buffer;
char *end;
char *fields[MAXFIELDS];
int i = 0;
int n = 0;
char *match = NULL;

while (end = strpbrk(start, ",")) // Get pointer to next delimiter 
{
  /* found it, allocate enough space for it and NUL */
  /* If there ar two consecutive delimiters, only the NUL gets entered */
  n = end - start;
  match = malloc(n + 1);

  /* copy and NUL terminate */
  /* Note that if n is 0, nothing will be copied so do not need to test */
  memcpy(match, start, n);
  match[n] = '\0';

  printf("Found field entry: %s\n", match);
  /* Now save the actual match string pointer into the fields array*/
  /* Since the match pointer is in fields, it does not need to be freed */
  fields[i++] = match;
  start = end + 1;
}

/* Check that the last element in the gps_string is not ,
   Then get the final field, which has the NUL termination of the string */
  n = strlen(start);
  match = malloc(n + 1);
  /* Note that if n is 0, only the terminator will be put in */
  strcpy(match, start);
  printf("Found field entry: %s\n", match);
  fields[i++] = match;
  printf("Total number of fields: %d\n", i);
Community
  • 1
  • 1
sabbahillel
  • 4,357
  • 1
  • 19
  • 36
  • Curious: Any particular reason to use `strncpy(match, start, n)` vs `memcpy(match, start, n)` as code knowns copying will stop at `n` rather than at maybe a `'\0'`? – chux - Reinstate Monica Feb 05 '16 at 18:42
  • No particular reason. I just happen to use `strncpy()`. I suppose because I tend to think of `strcpy()` when I deal with strings. Actually, I do know that there is no '\0' in the substring. Good point though. I will change it. – sabbahillel Feb 05 '16 at 19:08
  • @chux Sorry forgot the ping. I changed the code to use `memcpy()`. Just a matter of habit of using `strcpy()` for strings that had me typing in `strncpy()` – sabbahillel Feb 05 '16 at 19:18
  • @abdullahcinar I just realized that I had forgotten to put the next delimiter find into the while. Without it, it would have been an infinite loop. Sorry about that – sabbahillel Feb 07 '16 at 00:13