0

I have a string in the format:

char *sampleLine = "name1: 251 name2: 23 name3: -67 name4: 0.00 name5: 310 name6: 410 name7: 54001 name8: 332 name9: SOME_TEXT name10: 3 name1: 181 235 237 name11: 11 name12: 240 241 242 243 244 245 246 247 248 249 250 name13: 0 name14: 2 name15: 1 name16: 0 name17: 6 name18: 0 name19: 500 name20: 200 name21: 64 name22: 1 name23: 6 name24: 0 name25: 0";

One of the issues with the string is that some of the names are repeated but the basic pattern seemed to be name: value. So I wrote an algorithm that would take a name and return a value but it doesn't seem to work and does not take into account the issue with a name being repeated.

So for example: if I pass in name1, I would expect to get 251 ,etc.

Here is the code with a sample main:

#include <stdio.h>                                                              
#include <stdlib.h>                                                             
#include <string.h>                                                             
#include <strings.h> 

char* extractValue(char* name, char* buffer)
{
    char* begining = strstr(buffer,name);
    begining += strlen(name) + 2;

    if (begining != NULL)
    {
        char* end = strstr(begining,":");

        if (end != NULL)
        {
            end += 1;

            for (int i=0; i < strlen(end); i++)
            {
                if (end[i] != ':')
                {
                    i++;
                } else {
                    char namevalue[200];
                    bzero(namevalue,200);

                    strncpy(namevalue,begining,i);

                    for (int x=strlen(namevalue); x>0; x--)
                    {
                        if (namevalue[x] == ' ')
                        {
                            char* value = (char*)malloc(200);
                            bzero(value,200);

                            strncpy(value,namevalue,strlen(namevalue) - (strlen(namevalue) - x));

                            return value;
                        }
                    }
                    break;
                }
            }
        }
    }
    return NULL;
}


int main (int argc, char** argv)
{
    char *sampleLine = "name1: 251 name2: 23 name3: -67 name4: 0.00 name5: 310 name6: 410 name7: 54001 name8: 332 name9: SOME_TEXT name10: 3 name1: 181 235 237 name11: 11 name12: 240 241 242 243 244 245 246 247 248 249 250 name13: 0 name14: 2 name15: 1 name16: 0 name17: 6 name18: 0 name19: 500 name20: 200 name21: 64 name22: 1 name23: 6 name24: 0 name25: 0";

    char* value1  = extractValue("name1", sampleLine);
    char* value3  = extractValue("name3", sampleLine);
    char* value17 = extractValue("name17", sampleLine);

    printf("value 1 = %s\n",value1);
    printf("value 3 = %s\n",value3);
    printf("value 17 = %s\n",value17);

    return 0;
}

When I run it, I get:

$ gcc -Wall -std=c99 -o parse parse.c && ./parse
value 1 = 251 name2: 23
value 3 = -67 name4: 0.00
value 17 = 6 name18: 0 name19: 500 name20: 200 name21:

Instead of expected

value 1 = 251
value 3 = -67
value 17 = 6
izidor
  • 4,068
  • 5
  • 33
  • 43
Joel Parker
  • 295
  • 1
  • 4
  • 17

1 Answers1

0

Two subtle errors.

First,

if (end[i] != ':')
{
   i++;
} else ..

By manually incrementing i here, you are skipping a character, as i already gets incremented by the for-loop. It appears to have no side effects, but only because ...

Second: the actual cause of your problem is you are measuring the length of the wrong string. You locate the beginning of the name value (begining) and then its end by scanning forward from that position for the next :. Then, you track backwards to find the previous space, which should be the end of the value. But ...

for (i=0; i < strlen(end); i++)

checks forwards from the (already determined) end of the string! Of course, for your test names, you are bound to find a colon somewhere further along--but its position has nothing to do with the string you are interested in, which is between begining and end.

Change your i loop to

for (i=0; i < end-begining; i++)

and change the : check to

if (begining[i] == ':')
{
   char namevalue[200];
   ... (etc.)

(discarding the i++ line).


Loose notes

A slightly faster way to look for a single character is strchr:

char* end = strchr(begining,':');

You might want to find a better strategy for locating the name. If you are looking for name1, it could find name12 as well as noname1.

One way would be to split a string into tokens using strtok. Then anything ending with a : is a potential name, and the next token is the value you are looking for. Omitted, because it's a nice exercise in its own. (And if you are going to try this: strtok modifies the original string!)

Finally, you can do without all those loops :-)

char* extractValue(char* name, char* buffer)
{
    char *start, *end, *ret_val;
    int length;
    char* begining = strstr(buffer,name);
    if (!begining)
        return NULL;
    start = begining + strlen(name);
    if (start[0] != ':')
        return NULL;
    start++;

//  skip initial spaces, if any
    while (*start == ' ')
        start++;
//  ptr points to the start of data. Find end.
    end = start;
    while (*end && *end != ' ')
        end++;

//  we have the correct string in start..end
    length = end-start;

//  it's a zero-terminated string so add 1 for the zero
    ret_val = (char *)malloc(length+1);
    strncpy (ret_val, start, length);
//  put the zero where it belongs
    ret_val[length] = 0;

    return ret_val;
}
Community
  • 1
  • 1
Jongware
  • 22,200
  • 8
  • 54
  • 100
  • This works great for something like name2: 23 but doesn't work for the case where name12: 240 241 242 243 244 245 246 247 248 249 250 which only return 240. Any thoughts how to extract both types of values ? – Joel Parker Oct 20 '13 at 15:05
  • In that case you can use your own idea: from `begining`, scan *forward* for the next `:`; if found, scan *backwards* again for a space. Change this where I update `end`, `start` is still good. A plus point, by the way, would be that if you have `nameX:` but no data it returns an empty string. My code returns the next name. – Jongware Oct 20 '13 at 15:48