c从文件读入数组并拆分

时间:2013-11-23 07:34:21

标签: c malloc fread

我有一个特定的txt文件(例如 - dic.txt),其中的单词按此顺序出现:

hello - ola - hiya \n
chips - fries - frenchfries \n

我需要将文件的内容读入一个字符串数组数组: 例如:

array[0]  : [hello,ola,hiya]
array[1]  : [chips,fries,frenchfries]

我正在考虑使用strtok将文件中的每一行拆分成一个字符串(将整个文件复制到一个字符串中并计算行数),但我无法想象如何拆分将每一行("hello - ola - hiya \n")放入单词中,并将每个数组存储到数组中(数组中的字符串数组)。

我正在考虑使用malloc来为每行单词分配内存,并将指向字符串数组的指针存储到数组中,但我很乐意收到任何建议。

2 个答案:

答案 0 :(得分:2)

从文件中读取行然后将其拆分为标记的直接方法是使用fgets读取行,然后使用strtok将每行拆分为标记:

int main(int argc, char *argv[])
{
    // Check for arguments and file pointer omitted
    FILE *f = fopen(argv[1], "r");

    for (;;) {
        char line[80];
        char *token;

        if (fgets(line, 80, f) == NULL) break;
        token = strtok(line, " -\n");
        while (token) {
            // Do something with token, for example:
            printf("'%s' ", token);
            token = strtok(NULL, " -\n");
        }
    }

    fclose(f);
    return 0;
}

只要文件中的所有行都短于80个字符,这种方法就可以了。它适用于每行可变数量的令牌。

您已经提到了处理线路内存的问题。上面的示例假定内存处理由每个单词的数据结构完成。 (这不是示例的一部分,它只打印令牌。)

每行可以malloc内存,这比每行严格的字符限制更灵活,但最终会有很多分配。好处是你的单词不需要额外的内存,它们只能成为行的指针,但你必须注意为行准确分配内存 - 然后释放它。

如果你把整个文本文件读到一个连续的内存块,你基本上就完成了内存存储,只要你保持那个块“活着”,只要你的文字存在:

char *slurp(const char *filename, int *psize)
{
    char *buffer;
    int size;
    FILE *f;

    f = fopen(filename, "r");
    if (f == NULL) return NULL;

    fseek(f, 0, SEEK_END);
    size = ftell(f);
    fseek(f, 0, SEEK_SET);

    buffer = malloc(size + 1);
    if (buffer) {
        if (fread(buffer, 1, size, f) < size) {
            free(buffer);
        } else {
            buffer[size] = '\0';
            if (psize) *psize = size;
        }
    }

    fclose(f);
    return buffer;
}

有了这块内存,您可以先查找下一个换行符,然后使用上面的strtok

int main(int argc, char *argv[])
{
    char *buffer;    // contiguous memory chunk
    char *next;      // pointer to next line or NULL for last line

    buffer = slurp(argv[1], NULL);    
    if (buffer == NULL) return 0;

    next = buffer;
    while (next) {
        char *token;
        char *p = next;

        // Find beginning of the next line, 
        // i.e. the char after the next newline
        next = strchr(p, '\n');
        if (next) {
            *next = '\0';      // Null-terminate line
            next = next + 1;   // Advance past newline
        }

        token = strtok(p, " -\n");
        while (token) {
            // Do something with token, for example:
            printf("'%s' ", token);
            token = strtok(NULL, " -\n");
        }        
    }

    free(buffer);             // ... and invalidate your words        
    return 0;
}

如果使用fscan,则始终将找到的标记复制到临时缓冲区,当您将它们存储在字典结构中时,必须使用strcpy再次复制它们。这是很多复制。在这里,您只需读取并分配一次,然后使用指向块的指针。 strtok null - 终止标记,因此您的块是C字符串链。

将整个文件读入内存通常不是一个好的解决方案,但在这种情况下,基本上的文件数据,这是有意义的。

(注意:关于内存的所有讨论都不会影响字典结构所需的内存,树中的节点和列表中的节点等等。它只是存储正确的字符串。)

答案 1 :(得分:1)

使用fgets

int eol(int c, FILE *stream) //given a char and the file, check if eol included
{
    if (c == '\n')
        return 1;
    if (c == '\r') {
        if ((c = getc(stream)) != '\n')
            ungetc(c, stream);
        return 1;
    }
    return 0;
}

int charsNumInLine(FILE *stream)
{
    int position = ftell(stream);
    int c, num_of_chars=0;

    while ((c = getc(stream)) != EOF && !eol(c, stream))
        num_of_chars++;

    fseek(stream,position,SEEK_SET); //get file pointer to where it was before this function call
    return num_of_chars; 
}

void main()
{
    //...
    char *buffer;
    int size;
    while()
    {
        size=charsNumInLine(stream);
        buffer = (char*)malloc( size*sizeof(char) );
        fgets(buffer,sizeof(buffer),stream);
        if (feof(stream) || ferror(stream) )
            break;

        // use strtok to separate words...
    }
    //...
}

另一种方法是使用fscanf(file,"%s",buff)来阅读单词,然后使用上面的函数eol来查看我们何时到达换行符。