我习惯于使用PHP,但我开始学习C语言。我想创建一个程序,逐行读取一个文件,并将每行存储到一个数组中。
到目前为止,我已经有了一个逐行读取文件的程序,甚至还能边读边打印每一行,但现在我只需要把每一行都添加到一个数组中。
昨天晚上我的朋友跟我说了一下这个问题。他说我必须使用C语言中的多维数组,所以基本上是 array[x][y]
. 该 [y]
部分本身很容易,因为我知道每行的最大字节数。然而,我不知道有多少个 行 的文件。
我想我可以让它在文件中循环,每次只增加一个整数,然后使用它,但我觉得可能有一个更简单的方法。
有什么想法,甚至是一个正确的提示吗?我很感激任何帮助。
要动态分配一个二维数组。
char **p;
int i, dim1, dim2;
/* Allocate the first dimension, which is actually a pointer to pointer to char */
p = malloc (sizeof (char *) * dim1);
/* Then allocate each of the pointers allocated in previous step arrays of pointer to chars
* within each of these arrays are chars
*/
for (i = 0; i < dim1; i++)
{
*(p + i) = malloc (sizeof (char) * dim2);
/* or p[i] = malloc (sizeof (char) * dim2); */
}
/* Do work */
/* Deallocate the allocated array. Start deallocation from the lowest level.
* that is in the reverse order of which we did the allocation
*/
for (i = 0; i < dim1; i++)
{
free (p[i]);
}
free (p);
修改上面的方法 当你需要添加另一行时,做 *(p + i) = malloc (sizeof (char) * dim2);
并更新 i
. 在这种情况下,你需要预测文件中的最大行数,这是由 dim1
变量,我们为其分配 p
数组。这将只分配 (sizeof (int *) * dim1)
字节,因此比 char p[dim1][dim2]
(在C99)。
我想还有一种方法。将数组分配在块中,当有溢出时,将它们链起来。
struct _lines {
char **line;
int n;
struct _lines *next;
} *file;
file = malloc (sizeof (struct _lines));
file->line = malloc (sizeof (char *) * LINE_MAX);
file->n = 0;
head = file;
在这之后,第一个块就可以使用了。当你需要插入一行的时候就可以了。
/* get line into buffer */
file.line[n] = malloc (sizeof (char) * (strlen (buffer) + 1));
n++;
当你需要插入一行的时候 n
是 LINE_MAX
分配另一个块,并将其链接到这个块。
struct _lines *temp;
temp = malloc (sizeof (struct _lines));
temp->line = malloc (sizeof (char *) * LINE_MAX);
temp->n = 0;
file->next = temp;
file = file->next;
就像这样。
当一个块的 n
成为 0
块指针,将其重新定位,并更新当前的块指针。file
到前一个。你可以从开始的单链接列表开始遍历,也可以使用双链接。
C语言中没有标准的可调整数组类型,你必须自己实现它,或者使用第三方库。 这里有一个简单的赤裸裸的例子。
typedef struct int_array
{
int *array;
size_t length;
size_t capacity;
} int_array;
void int_array_init(int_array *array)
{
array->array = NULL;
array->length = 0;
array->capacity = 0;
}
void int_array_free(int_array *array)
{
free(array->array);
array->array = NULL;
array->length = 0;
array->capacity = 0;
}
void int_array_push_back(int_array *array, int value)
{
if(array->length == array->capacity)
{
// Not enough space, reallocate. Also, watch out for overflow.
int new_capacity = array->capacity * 2;
if(new_capacity > array->capacity && new_capacity < SIZE_T_MAX / sizeof(int))
{
int *new_array = realloc(array->array, new_capacity * sizeof(int));
if(new_array != NULL)
{
array->array = new_array;
array->capacity = new_capacity;
}
else
; // Handle out-of-memory
}
else
; // Handle overflow error
}
// Now that we have space, add the value to the array
array->array[array->length] = value;
array->length++;
}
像这样使用
int_array a;
int_array_init(&a);
int i;
for(i = 0; i < 10; i++)
int_array_push_back(&a, i);
for(i = 0; i < a.length; i++)
printf("a[%d] = %d\n", i, a.array[i]);
int_array_free(&a);
当然,这只是对一个数组来说的 int
s. 因为C语言没有模板,所以你必须把所有这些代码放在一个宏中,为每一个不同类型的数组编写(或者使用一个不同的预处理器,如 GNU m4). 或者,你也可以使用一个通用的数组容器,它可以使用 void*
指针(要求所有数组元素都是 malloc
ed)或不透明的内存 blobs,这就需要对每一个元素访问进行投递,并对其进行 memcpy
为每个元素getset。
无论如何,这都不好看。 二维数组就更丑了。
在这里,你也可以用链接列表代替数组,代码更简单,但分配更频繁,可能会受到碎片化的影响。
只要你不打算做很多随机访问(这里是O(n)),迭代就和普通数组一样简单。
typedef struct Line Line;
struct Line{
char text[LINE_MAX];
Line *next;
};
Line *mkline()
{
Line *l = malloc(sizeof(Line));
if(!l)
error();
return l;
}
main()
{
Line *lines = mkline();
Line *lp = lines;
while(fgets(lp->text, sizeof lp->text, stdin)!=NULL){
lp->next = mkline();
lp = lp->next;
}
lp->next = NULL;
}
如果你使用C语言,你需要自己实现数组的大小调整。C++和SDL已经为你完成了这个工作。这就是所谓的 vector
. http:/www.cplusplus.comreferencestlvector
虽然多维数组可以解决这个问题,但矩形二维数组就不是真正自然的C语言解决方案了。
这里有一个程序,最初将文件读入一个链接列表,然后分配一个大小合适的指针向量。然后,每个单独的字符确实会显示为 array[line][col]
但其实每一行都只需要长。它的C99除了 <err.h>
.
#include <err.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
typedef struct strnode {
char *s;
struct strnode *next;
} strnode;
strnode *list_head;
strnode *list_last;
strnode *read1line(void) {
char space[1024];
if(fgets(space, sizeof space, stdin) == NULL)
return NULL;
strnode *node = malloc(sizeof(strnode));
if(node && (node->s = malloc(strlen(space) + 1))) {
strcpy(node->s, space);
node->next = NULL;
if (list_head == NULL)
list_head = node;
else
list_last->next = node;
list_last = node;
return node;
}
err(1, NULL);
}
int main(int ac, char **av) {
int n;
strnode *s;
for(n = 0; (s = read1line()) != NULL; ++n)
continue;
if(n > 0) {
int i;
strnode *b;
char **a = malloc(n * sizeof(char *));
printf("There were %d lines\n", n);
for(b = list_head, i = 0; b; b = b->next, ++i)
a[i] = b->s;
printf("Near the middle is: %s", a[n / 2]);
}
return 0;
}
您可以使用 malloc
和 realloc
函数来动态地分配和调整一个指向 char
而数组中的每个元素将指向一个从文件中读取的字符串(其中该字符串的存储空间也是动态分配的)。 为了简单起见,我们假设每行的最大长度小于M个字符(算上新行),所以我们不必对每行进行任何动态调整。
每次扩展数组时,你需要手动跟踪数组的大小。 一个常见的技术是每次扩展时将数组的大小翻倍,而不是按固定的大小扩展;这样可以最大限度地减少调用 realloc
,这有可能是昂贵的。 当然,这意味着你必须跟踪两个数量:数组的总大小和当前读取的元素数量。
例子:
#define INITIAL_SIZE ... // some size large enough to cover most cases
char **loadFile(FILE *stream, size_t *linesRead)
{
size_t arraySize = 0;
char **lines = NULL;
char *nextLine = NULL;
*linesRead = 0;
lines = malloc(INITIAL_SIZE * sizeof *lines);
if (!lines)
{
fprintf(stderr, "Could not allocate array\n");
return NULL;
}
arraySize = INITIAL_SIZE;
/**
* Read the next input line from the stream. We're abstracting this
* out to keep the code simple.
*/
while ((nextLine = getNextLine(stream)))
{
if (arraySize <= *linesRead)
{
char **tmp = realloc(lines, arraysSize * 2 * sizeof *tmp);
if (tmp)
{
lines = tmp;
arraySize *= 2;
}
}
lines[(*linesRead)++] = nextLine;
)
return lines;
}