c splitting a char* into an char**

2020-04-14 07:53发布

I'm reading in a line from a file (char by char, using fgetc()), where all fields(firstname, lastname, ...) are seperated by an ;. What I now want to do is create a char**, add all the chars to that and replace the ; by \0 so that I effectively get a list of all the fields.

Is that actually possibly? And when I create a char**, e.g. char ** buf = malloc(80) can I treat it like a one dimensional array? If the memory returned by malloc contiguous?

EDIT

Sry, meant to replace ; by \0, bot \n.

EDIT 2

This code should demonstrate what I intend to do (may clarify things a little):

int length = 80; // initial length char *buf = malloc(length); int num_chars = 0;

// handle malloc returning NULL

// suppose we already have a FILE pointer fp for (int ch = fgetc(fp); ch != EOF && ferror(fp) == 0; ch = fgetc(fp)) {
    if (length == size) { // expand array if necessary
        length += 80;
        buf = realloc(buf, length); 
        // handle realloc failure
    }

    if (ch == ';') {
        buf[num_chars] = '\0'; // replace delimiter by null-terminator
    } else {
        buf[num_chars] = ch; // else put char in buf
    } }

// resize the buffer to chars read buf
= realloc(buf, num_chars);

// now comes the part where I'm quite unsure if it works / is possible

char **list = (char **)buf; // and now I should be able to handle it like a list of strings?

标签: c
4条回答
欢心
2楼-- · 2020-04-14 08:12

It may be that Helper Method wants each of the "fields" written to a nul-terminated string. I cannot tell. You can do that with strtok(). However strtok() has problems - it destroys the original string "fed" to it.

#define SPLIT_ARRSZ 20   /* max possible number of fields */
char **
split( char *result[], char *w, const char *delim)
{
    int i=0;
    char *p=NULL;
    for(i=0, result[0]=NULL, p=strtok(w, delim); p!=NULL; p=strtok(NULL, delim), i++ )
    {
           result[i]=p;
           result[i+1]=NULL;
    }
    return result;
}

void
usage_for_split(void)
{
    char *result[SPLIT_ARRSZ]={NULL};
    char str[]="1;2;3;4;5;6;7;8;9;This is the last field\n";
    char *w=strdup(str);
    int i=0;
    split(result, w, ";\n");
    for(i=0; result[i]!=NULL; i++)
       printf("Field #%d = '%s'\n", i, result[i]);
    free(w);
}
查看更多
我只想做你的唯一
3楼-- · 2020-04-14 08:16

It's not exactly possible as you describe it, but something similar is possible. More specifically, the last line of your example char **list = (char **)buf; won't do what you believe. char **list means items pointed by *list will be of the type char*, but the content of *buf are chars. Hence it won't be possible to change one into another.

You should understand that a char ** is just a pointer, it can hold nothing by itself. But the char ** can be the start address of an array of char *, each char * pointing to a field.

You will have to allocate space for the char * array using a first malloc (or you also can use a static array of char * instead of a char**. You also will have to find space for every individual field, probably performing a malloc for each field and copying it from the initial buffer. If the initial buffer is untouched after reading, it would also be possible to use it to keep the field names, but if you just replace the initial ; by a \n, you'll be missing the trailing 0 string terminator, you can't replace inplace one char by two char.

Below is a simplified example of what can be done (removed malloc part as it does not add much to the example, and makes it uselessly complex, that's another story):

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

main(){
    // let's define a buffer with space enough for 100 chars
    // no need to perform dynamic allocation for a small example like this
    char buf[100];
    const char * data = "one;two;three;four;";

    // now we want an array of pointer to fields, 
    // here again we use a static buffer for simplicity,
    // instead of a char** with a malloc
    char * fields[10];
    int nbfields = 0;
    int i = 0;
    char * start_of_field;

    // let's initialize buf with some data, 
    // a semi-colon terminated list of strings 
    strcpy(buf, data);

    start_of_field = buf;
    // seek end of each field and
    // copy addresses of field to fields (array of char*)
    for (i = 0; buf[i] != 0; i++){
        if (buf[i] == ';'){
            buf[i] = 0;
            fields[nbfields] = start_of_field;
            nbfields++;
            start_of_field = buf+i+1;
        }
    }

    // Now I can address fields individually
    printf("total number of fields = %d\n"
           "third field is = \"%s\"\n",
           nbfields, fields[2]);
}
查看更多
Emotional °昔
4楼-- · 2020-04-14 08:21

Yes, it's possible. Yes, you can treat it as a one dimensional array. Yes, the memory is contiguous.

But you probably want:

char ** fields = malloc(sizeof(char *) * numFields);

Then you can do:

// assuming `field` is a `char *`
fields[i++] = field;
查看更多
老娘就宠你
5楼-- · 2020-04-14 08:23

I now want to do is create a char**, add all the chars to that

You would allocate an array of char (char*) to store the characters, not an array of char* (char**).

And when I create a char*, e.g. char * buf = malloc(80) can I treat it like a one dimensional array? If the memory returned by malloc contiguous?

Yes, malloc always returns continguous blocks. Yes, you can treat it as single-dimensional an array of char* (with 80/sizeof char* elements). But you'll need to seperately allocate memory for each string you store in this array, or create another block for storing the string data, then use your first array to store pointers into that block.

(or something else; lots of ways to skin this cat)

查看更多
登录 后发表回答