Duplication?

Started by
16 comments, last by Zahlman 15 years, 3 months ago
char *task_serialize(Task *task){    // Add one for the camma    int size = strlen(task->task);    size += strlen(task->project);    size += strlen(task->context);    // two for priority    size += 1;    // two for done bool    size += 1;    // two for nextAction bool and cammas    size += 8;        char *result = malloc(size);        //memcpy((void *)result, (void *)task, sizeof(task));    sprintf( result, "%s,%s,%s,%i,%i,%i|",            task->task,            task->project,            task->context,            task->priority,            task->done,            task->nextAction);                                                            return result;}


Output:
Check emai,Check email,none,comp,Check email,none,computer,0,1,1|,0,0,0|

Should be:
Check email,none,computer,0,1,1|

Problem with my parsing code?

Advertisement
This is the value of a free standing, compilable example.

This Works For MeTM.

(note: minor modifications made to compile under a c++ compiler).
#include <cstdio>#include <cstring>#include <cstdlib>typedef enum {NORMAL, LOW, MEDIUM, HIGH} priority;typedef struct{    char *task;    char *project;    char *context;    priority priority;    bool done;    bool nextAction;} Task;Task *make_task(const char *task,const char *project,const char *context,priority p,bool done,bool nextAction){    Task *t = (Task *)malloc(sizeof(Task));    t->task = _strdup(task);    t->project = _strdup(project);    t->context = _strdup(context);    t->priority = p;    t->done = done;    t->nextAction = nextAction;	return t;}void free_task(Task *t){    if(t)    {        free(t->task);        free(t->project);        free(t->context);        free(t);    }}const char *task_serialize(Task *task){    // Add one for the camma    int size = strlen(task->task);    size += strlen(task->project);    size += strlen(task->context);    // two for priority    size += 1;    // two for done bool    size += 1;    // two for nextAction bool and cammas	size += 8;        char *result = (char *)malloc(size);        //memcpy((void *)result, (void *)task, sizeof(task));    sprintf( result, "%s,%s,%s,%i,%i,%i|",            task->task,            task->project,            task->context,            task->priority,            task->done,            task->nextAction);                                                            return result;}int main(){	Task *task = make_task("Check email","none","computer",NORMAL,1,1);	const char *string = task_serialize(task);	printf("%s\n",string);	free_task(task);}

Output:
Quote:
Check email,none,computer,0,1,1|
Actually in your code I don't see where you are doing anything to data. Aren't all these lines finding the same , character?

i = strchr(data, ',');
Hmm you're right. After I read the data upto the first comma, is there a way to cut that out? It didn't like me copyring the string from data back into data using strcpy.

ripoff,load_task is ment to load a line from a txt file. So I think my problem is in the code parsing, as visitor said.
I presume I will just have to coppy it to a new array, everytime I finish reading a part of the data string.

Unless what I do is create a pointer to a character, then just keep free()ing the number of characters I don't need anymore.
So I discovered strtok(), but every time I use it, it returns a stinking bus error. This is getting on my nerves. Maybe I'll switch back to my comfy cozy C++.
strtok modifies the input string (inserts '\0' at the beginning of delimiter series / end of token). So if you happen to pass in a string literal (which is unmodifyable) you can get errors since the string literals often are in unmodifyable memory. Whenever a function accepts a literal string, the argument must be const (const char*), then the compiler would tell you (hopefully, in C const errors don't seem to always be compiler errors...).

Perhaps, what you might do is make a copy of the input string (allocated dynamically and added as a struct member) and the rest of the struct members simply point at tokens in this string (no additional memory allocation required).

Of course, if you don't need to do this in C, C++ might have more convenient tools for tokenizing and concatenation.
Your parsing code has problems as well.

For one, you copy and paste code. Don't do that. Write a helper function to "copy from the string up to the next comma". This, in turn, can make use of standard C library functions.

For another, you set the Task object's members to point at the buffers you've malloc()'ed, and then immediately free() them. This is bad. The pointers are now immediately invalid. The whole point of free() is to say "I don't need the pointed-at thing any more". This is clearly not what you intend. That's only appropriate for reading the integer values, where you throw away the string after getting the integer.

/* EDIT: Expecting to be able to modify the input is a needless restriction;   thanks to visitor for pointing out the value of avoiding that. *//* EDIT 2: shortened logic, taking full advantage of my clever idea to copy   the pointer. ;) */char *next_item(char **begin_ptr) {  /* before: *begin_ptr points at a string (possibly in the middle).     after: *begin_ptr points just past the next comma, and the return value is            a newly allocated string representing everything up to the first            comma.     Repeatedly calling this, therefore, will extract each comma-delimited     item of the string, as *begin is updated to the appropriate place each     time. (You could also loop while the pointer is non-NULL.)  */  char *result, *begin = *begin_ptr, end;  if (!begin) return NULL;  /* Find the comma, if any, and update the out-parameter to point past it     (or set to NULL if there is no comma). 'begin' is untouched by this */  end = strchr(begin, ',');  if (end) {    *begin_ptr = end + 1;  } else {    *begin_ptr = NULL;    end = begin + strlen(begin);  }  /* Copy the appropriate string data and return it. */  result = malloc(end - begin);  strncpy(result, begin, end - begin);  return result;}  int next_item_as_int(char **begin_ptr) {  int result;  char *result_str = next_item(begin_ptr);  if (!result_str) return 0;  result = atoi(result_str);  free(result_str);  return result;}Task *load_task(char *data) {  /* BTW, C89 doesn't allow you to declare variables in the middle of a scope     (at least if the compiler is strict), although C99 does. */  Task *result = malloc(sizeof(Task));  char *priority_str, *done_str, *next_str;  result->task = next_item(&data);  result->project = next_item(&data);  result->context = next_item(&data);  result->priority = next_item_as_int(&data);  result->done = next_item_as_int(&data);  result->nextAction = next_item_as_int(&data);  return result;}


Hopefully this gives you a few good ideas about how to organize code, particularly as concerns memory management. Many of these ideas apply to C++ as well, although in C++ you have a lot of other more powerful tools too.

This topic is closed to new replies.

Advertisement