1

I'm trying to split a string every X amount of characters, and then store each line in an array of structs. However, I'm wondering what would be a short and efficient way of doing it. I thought that maybe I could use sscanf, but not very sure how to. Any help will be appreciated. So far I have:

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

struct st {char *str;};

int main ()
{
   struct st **mystruct;

   char tmp[] = "For configuration options (arch/xxx/config.in, and all the Config.in files),somewhat different indentation is used.";
   size_t max = 20, j = 0; // max length of string
   size_t alloc = strlen(tmp)/max + 1;

   mystruct = malloc(alloc * sizeof *mystruct);
   for (j = 0; j < alloc; j++)
      mystruct[j] = malloc(sizeof *mystruct[j]);

   const char *ptr = tmp;
   char field [ max ];
   int n;

   while (*ptr != '\0') {
        int line = sscanf(ptr, "%s", field, &n); // not sure how to use max in here
        mystruct[j]->str = field;
        field[0]='\0';
        if (line == 1)
            ptr += n;
        if ( n != max )
            break;
        ++ptr;
        ++j;
    }

   return 0;
}

So when I iterate over my struct, I can get something like:

For configuration op
tions (arch/xxx/conf
ig.in, and all the C
onfig.in files),some
what different inden
tation is used.

5 Answers 5

3

You could use strncpy.

FYI:

char field [ max ];
while (...) {
    mystruct[j]->str = field;

Two problems with this: (1) every struct in your array is going to end up pointing at the same string, which will have the value of the last thing you scanned, (2) they are pointing to a variable on the stack, so when this function returns they will be trashed. That doesn't manifest itself visibly here (e.g. your program doesn't explode) because the function happens to be 'main', but if you moved this to a separate routine and called it to parse a string, you'd get back garbage.

mystruct doesn't need to be pointer to pointer. For a 1D array, just allocate a block N * sizeof *myarray for N elements.

A common C idiom when dealing with structs is to use typedef so you don't have to type struct foo all the time. For instance:

typedef struct { 
   int x, y;
} point;

Now instead of typing struct point pt you can just say point pt.

Sign up to request clarification or add additional context in comments.

1 Comment

could you elaborate a little bit more when you say using strncpy? thanks
0

If your string is not going to change after you split it up, I'd recommend using a struct like this:

struct st {
    char *begin;
    char *end;
};

or the alternative:

struct st {
    char *s;
    size_t len;
};

Then instead of creating all those new strings, just mark where each one begins and ends in your struct. Keep the original string in memory.

5 Comments

I am not fond of these ideas, because in all cases these char * would not point to properly null-terminated strings representing the individual lines. So, for instance, if he were to loop over the structs and call something like printf("%s\n",s.begin), the output would not be as desired.
@Dave: Do this: const char *p = s.begin; while( p != s.end ) putchar(*p++);
@Dave: Or using the start and len version: printf("%.*s\n", s.len, s.s);
@Dave: And even one more comment: in this day and age, no C/C++ programmer anywhere should be relying on null-terminated strings. No, no one.
+1, I'd use the second version of the struct. You can always make a "real" string from it by using strncpy to a buffer of size len + 1 and appending the '\0' yourself (or sprintf with %.*s).
0

One option is to do it character-by-character.

Calculate the number of lines as you are currently doing.

Allocate memory = (strlen(tmp) + number_of_lines) * sizeof(char)

Walk through your input string, copying characters from the input to the newly allocated memory. Every 20th character, insert a null byte to delimit that string. Save a pointer to the beginning of each line in your array of structs.

Comments

0

Its easy enough?

#define SMAX 20
typedef struct {char str[SMAX+1];} ST;

int main()
{
  ST st[SMAX]={0};
  char *tmp = "For configuration options (arch/xxx/config.in, and all the Config.in files),somewhat different indentation is used.";
  int i=0,j;
  for( ; (st[i++]=*(ST*)tmp).str[SMAX]=0 , strlen(tmp)>=SMAX; tmp+=SMAX );

  for( j=0;j<i;++j )
    puts(st[j].str);

  return 0;
}

Comments

0

You may use (non C standard but GNU) function strndup().

#define _GNU_SOURCE
#include <string.h>

struct st {char *str;};

int main ()
{
   struct st *mystruct; /* i wonder if there's need for double indirection... */

   char tmp[] = "For configuration options (arch/xxx/config.in, and all the Config.in files),somewhat different indentation is used.";
   size_t max = 20, j = 0; // max length of string
   size_t alloc = (strlen(tmp) + max - 1)/max; /* correct round up */

   mystruct = malloc(alloc * sizeof mystruct);
   if(!mystruct) return 1; /* never forget testing if allocation failed! */

   for(j = 0; j<alloc; j++)
   {
      mystruct[j].str = strndup(tmp+alloc*max, max);
   }
}

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.